From 9653255cffb2ca3c3356cff14ae85106767f3985 Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Fri, 20 Oct 2017 17:15:17 +0000 Subject: [PATCH] Merged in juniskane/nuttx_stm32l4/stm32l1_stm32l4_rtc_update_pr (pull request #514) STM32L1, STM32L4 RTC: add periodic interrupts, update L1 RTC implementation * STM32L4 RTC: add support experimental CONFIG_RTC_PERIODIC * STM32 RTC: separate STM32L1 RTC into a separate file STM32L1 RTC is very close to F4 or L4 versions, with two alarms and periodic wakeup support so backported L4 peripheral to L1. * RTC: add periodic alarms to upper and lower halves Approved-by: Gregory Nutt --- arch/arm/src/stm32/Make.defs | 3 + arch/arm/src/stm32/stm32_exti.h | 23 + arch/arm/src/stm32/stm32_exti_wakeup.c | 153 ++ arch/arm/src/stm32/stm32_rtc.c | 4 +- arch/arm/src/stm32/stm32_rtc.h | 4 +- arch/arm/src/stm32/stm32_rtc_lowerhalf.c | 195 +- arch/arm/src/stm32/stm32_rtcc.c | 86 +- arch/arm/src/stm32/stm32l15xxx_alarm.h | 173 ++ arch/arm/src/stm32/stm32l15xxx_rtcc.c | 1906 ++++++++++++++++++ arch/arm/src/stm32l4/Make.defs | 3 + arch/arm/src/stm32l4/stm32l4_adc.c | 2 +- arch/arm/src/stm32l4/stm32l4_exti.h | 25 +- arch/arm/src/stm32l4/stm32l4_exti_wakeup.c | 153 ++ arch/arm/src/stm32l4/stm32l4_rtc.c | 275 +++ arch/arm/src/stm32l4/stm32l4_rtc.h | 43 +- arch/arm/src/stm32l4/stm32l4_rtc_lowerhalf.c | 171 +- drivers/timers/rtc.c | 148 +- include/nuttx/timers/rtc.h | 72 +- 18 files changed, 3328 insertions(+), 111 deletions(-) create mode 100644 arch/arm/src/stm32/stm32_exti_wakeup.c create mode 100644 arch/arm/src/stm32/stm32l15xxx_alarm.h create mode 100644 arch/arm/src/stm32/stm32l15xxx_rtcc.c create mode 100644 arch/arm/src/stm32l4/stm32l4_exti_wakeup.c diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs index fdcbe7b0f2..e4402d0bfb 100644 --- a/arch/arm/src/stm32/Make.defs +++ b/arch/arm/src/stm32/Make.defs @@ -200,6 +200,9 @@ CHIP_CSRCS += stm32_rtc.c ifeq ($(CONFIG_RTC_ALARM),y) CHIP_CSRCS += stm32_exti_alarm.c endif +ifeq ($(CONFIG_RTC_PERIODIC),y) +CHIP_CSRCS += stm32_exti_wakeup.c +endif ifeq ($(CONFIG_RTC_DRIVER),y) CHIP_CSRCS += stm32_rtc_lowerhalf.c endif diff --git a/arch/arm/src/stm32/stm32_exti.h b/arch/arm/src/stm32/stm32_exti.h index 9ac20798df..8e46e239da 100644 --- a/arch/arm/src/stm32/stm32_exti.h +++ b/arch/arm/src/stm32/stm32_exti.h @@ -111,6 +111,29 @@ int stm32_exti_alarm(bool risingedge, bool fallingedge, bool event, xcpt_t func, void *arg); #endif +/**************************************************************************** + * Name: stm32_exti_wakeup + * + * Description: + * Sets/clears EXTI wakeup interrupt. + * + * Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returns: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +int stm32_exti_wakeup(bool risingedge, bool fallingedge, bool event, + xcpt_t func, void *arg); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/arch/arm/src/stm32/stm32_exti_wakeup.c b/arch/arm/src/stm32/stm32_exti_wakeup.c new file mode 100644 index 0000000000..9dd9ad7d99 --- /dev/null +++ b/arch/arm/src/stm32/stm32_exti_wakeup.c @@ -0,0 +1,153 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_exti_wakeup.c + * + * Copyright (C) 2009, 2012, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_exti.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Interrupt handlers attached to the RTC WAKEUP EXTI */ + +static xcpt_t g_wakeup_callback; +static void *g_callback_arg; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_exti_wakeup_isr + * + * Description: + * EXTI periodic WAKEUP interrupt service routine/dispatcher + * + ****************************************************************************/ + +static int stm32_exti_wakeup_isr(int irq, void *context, FAR void *arg) +{ + int ret = OK; + + /* Dispatch the interrupt to the handler */ + + if (g_wakeup_callback != NULL) + { + ret = g_wakeup_callback(irq, context, g_callback_arg); + } + + /* Clear the pending EXTI interrupt */ + + putreg32(EXTI_RTC_WAKEUP, STM32_EXTI_PR); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_exti_wakeup + * + * Description: + * Sets/clears EXTI wakeup interrupt. + * + * Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * + * Returns: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +int stm32_exti_wakeup(bool risingedge, bool fallingedge, bool event, + xcpt_t func, void *arg) +{ + g_wakeup_callback = func; + g_callback_arg = arg; + + /* Install external interrupt handlers (if not already attached) */ + + if (func) + { + irq_attach(STM32_IRQ_RTC_WKUP, stm32_exti_wakeup_isr, NULL); + up_enable_irq(STM32_IRQ_RTC_WKUP); + } + else + { + up_disable_irq(STM32_IRQ_RTC_WKUP); + } + + /* Configure rising/falling edges */ + + modifyreg32(STM32_EXTI_RTSR, + risingedge ? 0 : EXTI_RTC_WAKEUP, + risingedge ? EXTI_RTC_WAKEUP : 0); + modifyreg32(STM32_EXTI_FTSR, + fallingedge ? 0 : EXTI_RTC_WAKEUP, + fallingedge ? EXTI_RTC_WAKEUP : 0); + + /* Enable Events and Interrupts */ + + modifyreg32(STM32_EXTI_EMR, + event ? 0 : EXTI_RTC_WAKEUP, + event ? EXTI_RTC_WAKEUP : 0); + modifyreg32(STM32_EXTI_IMR, + func ? 0 : EXTI_RTC_WAKEUP, + func ? EXTI_RTC_WAKEUP : 0); + + return OK; +} diff --git a/arch/arm/src/stm32/stm32_rtc.c b/arch/arm/src/stm32/stm32_rtc.c index f26417b0d9..f78e2b3624 100644 --- a/arch/arm/src/stm32/stm32_rtc.c +++ b/arch/arm/src/stm32/stm32_rtc.c @@ -63,9 +63,11 @@ * the RTCC in these families. */ -#elif defined(CONFIG_STM32_STM32L15XX) || defined(CONFIG_STM32_STM32F20XX) || \ +#elif defined(CONFIG_STM32_STM32F20XX) || \ defined(CONFIG_STM32_STM32F30XX) # include "stm32_rtcc.c" +#elif defined(CONFIG_STM32_STM32L15XX) +# include "stm32l15xxx_rtcc.c" #elif defined(CONFIG_STM32_STM32F4XXX) # include "stm32f40xxx_rtcc.c" #endif diff --git a/arch/arm/src/stm32/stm32_rtc.h b/arch/arm/src/stm32/stm32_rtc.h index 81daa92659..fff697af45 100644 --- a/arch/arm/src/stm32/stm32_rtc.h +++ b/arch/arm/src/stm32/stm32_rtc.h @@ -66,8 +66,10 @@ /* Alarm function differs from part to part */ -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) # include "stm32f40xxx_alarm.h" +#elif defined(CONFIG_STM32_STM32L15XX) +# include "stm32l15xxx_alarm.h" #else # include "stm32_alarm.h" #endif diff --git a/arch/arm/src/stm32/stm32_rtc_lowerhalf.c b/arch/arm/src/stm32/stm32_rtc_lowerhalf.c index 6a445b80f9..027382e207 100644 --- a/arch/arm/src/stm32/stm32_rtc_lowerhalf.c +++ b/arch/arm/src/stm32/stm32_rtc_lowerhalf.c @@ -33,8 +33,6 @@ * ****************************************************************************/ -/* REVISIT: This driver is *not* thread-safe! */ - /**************************************************************************** * Included Files ****************************************************************************/ @@ -60,7 +58,7 @@ * Pre-processor Definitions ****************************************************************************/ -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) # define STM32_NALARMS 2 #else # define STM32_NALARMS 1 @@ -74,8 +72,8 @@ struct stm32_cbinfo_s { volatile rtc_alarm_callback_t cb; /* Callback when the alarm expires */ - volatile FAR void *priv; /* Private argurment to accompany callback */ -#ifdef CONFIG_STM32_STM32F4XXX + volatile FAR void *priv; /* Private argument to accompany callback */ +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) uint8_t id; /* Identifies the alarm */ #endif }; @@ -97,11 +95,19 @@ struct stm32_lowerhalf_s * this file. */ + sem_t devsem; /* Threads can only exclusively access the RTC */ + #ifdef CONFIG_RTC_ALARM /* Alarm callback information */ struct stm32_cbinfo_s cbinfo[STM32_NALARMS]; #endif + +#ifdef CONFIG_RTC_PERIODIC + /* Periodic wakeup information */ + + struct lower_setperiodic_s periodic; +#endif }; /**************************************************************************** @@ -126,6 +132,12 @@ static int stm32_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct lower_rdalarm_s *alarminfo); #endif +#ifdef CONFIG_RTC_PERIODIC +static int stm32_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo); +static int stm32_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -142,6 +154,10 @@ static const struct rtc_ops_s g_rtc_ops = .cancelalarm = stm32_cancelalarm, .rdalarm = stm32_rdalarm, #endif +#ifdef CONFIG_RTC_PERIODIC + .setperiodic = stm32_setperiodic, + .cancelperiodic = stm32_cancelperiodic, +#endif #ifdef CONFIG_RTC_IOCTL .ioctl = NULL, #endif @@ -177,7 +193,7 @@ static struct stm32_lowerhalf_s g_rtc_lowerhalf = ****************************************************************************/ #ifdef CONFIG_RTC_ALARM -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) static void stm32_alarm_callback(FAR void *arg, unsigned int alarmid) { FAR struct stm32_lowerhalf_s *lower; @@ -232,7 +248,7 @@ static void stm32_alarm_callback(void) } } -#endif /* CONFIG_STM32_STM32F4XXX */ +#endif /* CONFIG_STM32_STM32F4XXX || CONFIG_STM32_STM32L15XX */ #endif /* CONFIG_RTC_ALARM */ /**************************************************************************** @@ -396,11 +412,11 @@ static bool stm32_havesettime(FAR struct rtc_lowerhalf_s *lower) static int stm32_setalarm(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setalarm_s *alarminfo) { -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) FAR struct stm32_lowerhalf_s *priv; FAR struct stm32_cbinfo_s *cbinfo; struct alm_setalarm_s lowerinfo; - int ret = -EINVAL; + int ret; /* ID0-> Alarm A; ID1 -> Alarm B */ @@ -408,6 +424,13 @@ static int stm32_setalarm(FAR struct rtc_lowerhalf_s *lower, DEBUGASSERT(alarminfo->id == RTC_ALARMA || alarminfo->id == RTC_ALARMB); priv = (FAR struct stm32_lowerhalf_s *)lower; + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = -EINVAL; if (alarminfo->id == RTC_ALARMA || alarminfo->id == RTC_ALARMB) { /* Remember the callback information */ @@ -434,16 +457,25 @@ static int stm32_setalarm(FAR struct rtc_lowerhalf_s *lower, } } + nxsem_post(&priv->devsem); + return ret; #else FAR struct stm32_lowerhalf_s *priv; FAR struct stm32_cbinfo_s *cbinfo; - int ret = -EINVAL; + int ret; DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->id == 0); priv = (FAR struct stm32_lowerhalf_s *)lower; + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = -EINVAL; if (alarminfo->id == 0) { struct timespec ts; @@ -469,6 +501,8 @@ static int stm32_setalarm(FAR struct rtc_lowerhalf_s *lower, } } + nxsem_post(&priv->devsem); + return ret; #endif } @@ -495,7 +529,7 @@ static int stm32_setalarm(FAR struct rtc_lowerhalf_s *lower, static int stm32_setrelative(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setrelative_s *alarminfo) { -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) struct lower_setalarm_s setalarm; struct tm time; time_t seconds; @@ -646,17 +680,24 @@ static int stm32_setrelative(FAR struct rtc_lowerhalf_s *lower, #ifdef CONFIG_RTC_ALARM static int stm32_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) { -#ifdef CONFIG_STM32_STM32F4XXX +#if defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX) FAR struct stm32_lowerhalf_s *priv; FAR struct stm32_cbinfo_s *cbinfo; - int ret = -EINVAL; + int ret; DEBUGASSERT(lower != NULL); DEBUGASSERT(alarmid == RTC_ALARMA || alarmid == RTC_ALARMB); priv = (FAR struct stm32_lowerhalf_s *)lower; + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + /* ID0-> Alarm A; ID1 -> Alarm B */ + ret = -EINVAL; if (alarmid == RTC_ALARMA || alarmid == RTC_ALARMB) { /* Nullify callback information to reduce window for race conditions */ @@ -670,6 +711,8 @@ static int stm32_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) ret = stm32_rtc_cancelalarm((enum alm_id_e)alarmid); } + nxsem_post(&priv->devsem); + return ret; #else @@ -739,6 +782,130 @@ static int stm32_rdalarm(FAR struct rtc_lowerhalf_s *lower, } #endif +/**************************************************************************** + * Name: stm32_periodic_callback + * + * Description: + * This is the function that is called from the RTC driver when the periodic + * wakeup goes off. It just invokes the upper half drivers callback. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32_periodic_callback(void) +{ + FAR struct stm32_lowerhalf_s *lower; + struct lower_setperiodic_s *cbinfo; + rtc_wakeup_callback_t cb; + FAR void *priv; + + lower = (FAR struct stm32_lowerhalf_s *)&g_rtc_lowerhalf; + + cbinfo = &lower->periodic; + cb = (rtc_wakeup_callback_t)cbinfo->cb; + priv = (FAR void *)cbinfo->priv; + + /* Perform the callback */ + + if (cb != NULL) + { + cb(priv, 0); + } + + return OK; +} +#endif /* CONFIG_RTC_PERIODIC */ + +/**************************************************************************** + * Name: stm32_setperiodic + * + * Description: + * Set a new periodic wakeup relative to the current time, with a given + * period. This function implements the setperiodic() method of the RTC + * driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * alarminfo - Provided information needed to set the wakeup activity + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ +#ifdef CONFIG_RTC_PERIODIC +static int stm32_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo) +{ + FAR struct stm32_lowerhalf_s *priv; + int ret; + + ASSERT(lower != NULL && alarminfo != NULL); + priv = (FAR struct stm32_lowerhalf_s *)lower; + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + memcpy(&priv->periodic, alarminfo, sizeof(struct lower_setperiodic_s)); + + ret = stm32_rtc_setperiodic(&alarminfo->period, stm32_periodic_callback); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_cancelperiodic + * + * Description: + * Cancel the current periodic wakeup activity. This function implements + * the cancelperiodic() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id) +{ + FAR struct stm32_lowerhalf_s *priv; + int ret; + + DEBUGASSERT(lower != NULL); + priv = (FAR struct stm32_lowerhalf_s *)lower; + + DEBUGASSERT(id == 0); + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = stm32_rtc_cancelperiodic(); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -767,6 +934,8 @@ static int stm32_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_lowerhalf_s *stm32_rtc_lowerhalf(void) { + nxsem_init(&g_rtc_lowerhalf.devsem, 0, 1); + return (FAR struct rtc_lowerhalf_s *)&g_rtc_lowerhalf; } diff --git a/arch/arm/src/stm32/stm32_rtcc.c b/arch/arm/src/stm32/stm32_rtcc.c index b8e1c1a59e..08d09e2dfe 100644 --- a/arch/arm/src/stm32/stm32_rtcc.c +++ b/arch/arm/src/stm32/stm32_rtcc.c @@ -76,39 +76,11 @@ # error "CONFIG_STM32_PWR must selected to use this driver" #endif -#ifdef CONFIG_STM32_STM32L15XX -# if defined(CONFIG_RTC_HSECLOCK) -# error "RTC with HSE clock not yet implemented for STM32L15XXX" -# elif defined(CONFIG_RTC_LSICLOCK) -# error "RTC with LSI clock not yet implemented for STM32L15XXX" -# endif -#endif - /* Constants ************************************************************************/ #define SYNCHRO_TIMEOUT (0x00020000) #define INITMODE_TIMEOUT (0x00010000) -/* Proxy definitions to make the same code work for all the STM32 series ************/ - -#if defined(CONFIG_STM32_STM32L15XX) -# define STM32_RCC_XXX STM32_RCC_CSR -# define RCC_XXX_YYYRST RCC_CSR_RTCRST -# define RCC_XXX_RTCEN RCC_CSR_RTCEN -# define RCC_XXX_RTCSEL_MASK RCC_CSR_RTCSEL_MASK -# define RCC_XXX_RTCSEL_LSE RCC_CSR_RTCSEL_LSE -# define RCC_XXX_RTCSEL_LSI RCC_CSR_RTCSEL_LSI -# define RCC_XXX_RTCSEL_HSE RCC_CSR_RTCSEL_HSE -#else -# define STM32_RCC_XXX STM32_RCC_BDCR -# define RCC_XXX_YYYRST RCC_BDCR_BDRST -# define RCC_XXX_RTCEN RCC_BDCR_RTCEN -# define RCC_XXX_RTCSEL_MASK RCC_BDCR_RTCSEL_MASK -# define RCC_XXX_RTCSEL_LSE RCC_BDCR_RTCSEL_LSE -# define RCC_XXX_RTCSEL_LSI RCC_BDCR_RTCSEL_LSI -# define RCC_XXX_RTCSEL_HSE RCC_BDCR_RTCSEL_HSE -#endif - /************************************************************************************ * Private Data ************************************************************************************/ @@ -534,29 +506,6 @@ static void rtc_resume(void) #endif } -/************************************************************************************ - * Name: rtc_wkup_interrupt - * - * Description: - * RTC WKUP interrupt service routine through the EXTI line - * - * Input Parameters: - * irq - The IRQ number that generated the interrupt - * context - Architecture specific register save information. - * - * Returned Value: - * Zero (OK) on success; A negated errno value on failure. - * - ************************************************************************************/ - -#ifdef CONFIG_RTC_ALARM -static int rtc_wkup_interrupt(int irq, void *context) -{ -#warning "Missing logic" - return OK; -} -#endif - /************************************************************************************ * Public Functions ************************************************************************************/ @@ -610,54 +559,54 @@ int up_rtc_initialize(void) #ifdef CONFIG_RTC_HSECLOCK /* Use the HSE clock as the input to the RTC block */ - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_HSE); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_HSE); #elif defined(CONFIG_RTC_LSICLOCK) /* Use the LSI clock as the input to the RTC block */ - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_LSI); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_LSI); #elif defined(CONFIG_RTC_LSECLOCK) /* Use the LSE clock as the input to the RTC block */ - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_LSE); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_LSE); #endif /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */ - modifyreg32(STM32_RCC_XXX, 0, RCC_XXX_RTCEN); + modifyreg32(STM32_RCC_BDCR, 0, RCC_BDCR_RTCEN); } else /* The RTC is already in use: check if the clock source is changed */ { #if defined(CONFIG_RTC_HSECLOCK) || defined(CONFIG_RTC_LSICLOCK) || \ defined(CONFIG_RTC_LSECLOCK) - uint32_t clksrc = getreg32(STM32_RCC_XXX); + uint32_t clksrc = getreg32(STM32_RCC_BDCR); #if defined(CONFIG_RTC_HSECLOCK) - if ((clksrc & RCC_XXX_RTCSEL_MASK) != RCC_XXX_RTCSEL_HSE) + if ((clksrc & RCC_BDCR_RTCSEL_MASK) != RCC_BDCR_RTCSEL_HSE) #elif defined(CONFIG_RTC_LSICLOCK) - if ((clksrc & RCC_XXX_RTCSEL_MASK) != RCC_XXX_RTCSEL_LSI) + if ((clksrc & RCC_BDCR_RTCSEL_MASK) != RCC_BDCR_RTCSEL_LSI) #elif defined(CONFIG_RTC_LSECLOCK) - if ((clksrc & RCC_XXX_RTCSEL_MASK) != RCC_XXX_RTCSEL_LSE) + if ((clksrc & RCC_BDCR_RTCSEL_MASK) != RCC_BDCR_RTCSEL_LSE) #endif #endif { tr_bkp = getreg32(STM32_RTC_TR); dr_bkp = getreg32(STM32_RTC_DR); - modifyreg32(STM32_RCC_XXX, 0, RCC_XXX_YYYRST); - modifyreg32(STM32_RCC_XXX, RCC_XXX_YYYRST, 0); + modifyreg32(STM32_RCC_BDCR, 0, RCC_BDCR_BDRST); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_BDRST, 0); #if defined(CONFIG_RTC_HSECLOCK) /* Change to the new clock as the input to the RTC block */ - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_HSE); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_HSE); #elif defined(CONFIG_RTC_LSICLOCK) - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_LSI); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_LSI); #elif defined(CONFIG_RTC_LSECLOCK) - modifyreg32(STM32_RCC_XXX, RCC_XXX_RTCSEL_MASK, RCC_XXX_RTCSEL_LSE); + modifyreg32(STM32_RCC_BDCR, RCC_BDCR_RTCSEL_MASK, RCC_BDCR_RTCSEL_LSE); #endif putreg32(tr_bkp, STM32_RTC_TR); @@ -669,7 +618,7 @@ int up_rtc_initialize(void) /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */ - modifyreg32(STM32_RCC_XXX, 0, RCC_XXX_RTCEN); + modifyreg32(STM32_RCC_BDCR, 0, RCC_BDCR_RTCEN); } } @@ -786,12 +735,7 @@ int up_rtc_initialize(void) int stm32_rtc_irqinitialize(void) { #ifdef CONFIG_RTC_ALARM -# warning "Missing EXTI setup logic" - - /* Attach the ALARM interrupt handler */ - - irq_attach(STM32_IRQ_RTC_WKUP, rtc_interrupt, NULL); - up_enable_irq(STM32_IRQ_RTC_WKUP); +# warning "Missing logic" #endif return OK; diff --git a/arch/arm/src/stm32/stm32l15xxx_alarm.h b/arch/arm/src/stm32/stm32l15xxx_alarm.h new file mode 100644 index 0000000000..b433fff268 --- /dev/null +++ b/arch/arm/src/stm32/stm32l15xxx_alarm.h @@ -0,0 +1,173 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32l15xxx_alarm.h + * + * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. + * Author: Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_STM32L15XXX_ALARM_H +#define __ARCH_ARM_SRC_STM32_STM32L15XXX_ALARM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +typedef CODE void (*alm_callback_t)(FAR void *arg, unsigned int alarmid); + +/* These features are the same than in STM32F4 and STM32L4 */ + +enum alm_id_e +{ + RTC_ALARMA = 0, /* RTC ALARM A */ + RTC_ALARMB, /* RTC ALARM B */ + RTC_ALARM_LAST +}; + +/* Structure used to pass parameters to set an alarm */ + +struct alm_setalarm_s +{ + int as_id; /* enum alm_id_e */ + struct tm as_time; /* Alarm expiration time */ + alm_callback_t as_cb; /* Callback (if non-NULL) */ + FAR void *as_arg; /* Argument for callback */ +}; + +/* Structure used to pass parameters to query an alarm */ + +struct alm_rdalarm_s +{ + int ar_id; /* enum alm_id_e */ + FAR struct rtc_time *ar_time; /* Argument for storing ALARM RTC time */ +}; +#endif + +#ifdef CONFIG_RTC_PERIODIC +typedef CODE int (*wakeupcb_t)(void); +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +/**************************************************************************** + * Name: stm32_rtc_setalarm + * + * Description: + * Set an alarm to an absolute time using associated hardware. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_setalarm(FAR struct alm_setalarm_s *alminfo); + +/************************************************************************************ + * Name: stm32_rtc_rdalarm + * + * Description: + * Query an alarm configured in hardware. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int stm32_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo); + +/**************************************************************************** + * Name: stm32_rtc_cancelalarm + * + * Description: + * Cancel an alarm. + * + * Input Parameters: + * alarmid - Identifies the alarm to be cancelled + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_cancelalarm(enum alm_id_e alarmid); +#endif /* CONFIG_RTC_ALARM */ + +#ifdef CONFIG_RTC_PERIODIC +/**************************************************************************** + * Name: stm32_rtc_setperiodic + * + * Description: + * Set a periodic RTC wakeup + * + * Input Parameters: + * period - Time to sleep between wakeups + * callback - Function to call when the period expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_setperiodic(FAR const struct timespec *period, wakeupcb_t callback); + +/**************************************************************************** + * Name: stm32_rtc_cancelperiodic + * + * Description: + * Cancel a periodic wakeup + * + * Input Parameters: + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_cancelperiodic(void); +#endif /* CONFIG_RTC_PERIODIC */ + +#endif /* __ARCH_ARM_SRC_STM32_STM32L15XXX_ALARM_H */ diff --git a/arch/arm/src/stm32/stm32l15xxx_rtcc.c b/arch/arm/src/stm32/stm32l15xxx_rtcc.c new file mode 100644 index 0000000000..136cf8bc5a --- /dev/null +++ b/arch/arm/src/stm32/stm32l15xxx_rtcc.c @@ -0,0 +1,1906 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32l15xxx_rtcc.c + * + * Copyright (C) 2012-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "chip.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "up_arch.h" + +#include "stm32_rcc.h" +#include "stm32_pwr.h" +#include "stm32_exti.h" +#include "stm32_rtc.h" + +#ifdef CONFIG_RTC + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ +/* This RTC implementation supports + * - date/time RTC hardware + * - extended functions Alarm A and B + * */ + +#ifndef CONFIG_RTC_DATETIME +# error "CONFIG_RTC_DATETIME must be set to use this driver" +#endif + +#ifdef CONFIG_RTC_HIRES +# error "CONFIG_RTC_HIRES must NOT be set with this driver" +#endif + +#ifndef CONFIG_STM32_PWR +# error "CONFIG_STM32_PWR must selected to use this driver" +#endif + +#if defined(CONFIG_RTC_HSECLOCK) +# warning "RTC with HSE clock not yet tested on STM32L15XXX" +#elif defined(CONFIG_RTC_LSICLOCK) +# warning "RTC with LSI clock not yet tested on STM32L15XXX" +#endif + +#if !defined(CONFIG_RTC_MAGIC) +# define CONFIG_RTC_MAGIC (0xfacefeee) +#endif + +#if !defined(CONFIG_RTC_MAGIC_TIME_SET) +# define CONFIG_RTC_MAGIC_TIME_SET (CONFIG_RTC_MAGIC + 1) +#endif + +#if !defined(CONFIG_RTC_MAGIC_REG) +# define CONFIG_RTC_MAGIC_REG (0) +#endif + +#define RTC_MAGIC CONFIG_RTC_MAGIC +#define RTC_MAGIC_TIME_SET CONFIG_RTC_MAGIC_TIME_SET +#define RTC_MAGIC_REG STM32_RTC_BKR(CONFIG_RTC_MAGIC_REG) + +/* Constants ****************************************************************/ + +#define SYNCHRO_TIMEOUT (0x00020000) +#define INITMODE_TIMEOUT (0x00010000) + +#define RTC_ALRMR_ENABLE 0 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +typedef unsigned int rtc_alarmreg_t; + +struct alm_cbinfo_s +{ + volatile alm_callback_t ac_cb; /* Client callback function */ + volatile FAR void *ac_arg; /* Argument to pass with the callback function */ +}; +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +/* Callback to use when an EXTI is activated */ + +static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST]; +static bool g_alarm_enabled; /* True: Alarm interrupts are enabled */ +#endif + +#ifdef CONFIG_RTC_PERIODIC +static wakeupcb_t g_wakeupcb; +static bool g_wakeup_enabled; /* True: Wakeup interrupts are enabled */ +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* g_rtc_enabled is set true after the RTC has successfully initialized */ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int rtchw_check_alrawf(void); +static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg); +#if CONFIG_RTC_NALARMS > 1 +static int rtchw_check_alrbwf(void); +static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg); +#endif +static inline void rtc_enable_alarm(void); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtc_dumpregs + * + * Description: + * Disable RTC write protection + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_RTC_INFO +static void rtc_dumpregs(FAR const char *msg) +{ + rtcinfo("%s:\n", msg); + rtcinfo(" TR: %08x\n", getreg32(STM32_RTC_TR)); + rtcinfo(" DR: %08x\n", getreg32(STM32_RTC_DR)); + rtcinfo(" CR: %08x\n", getreg32(STM32_RTC_CR)); + rtcinfo(" ISR: %08x\n", getreg32(STM32_RTC_ISR)); + rtcinfo(" PRER: %08x\n", getreg32(STM32_RTC_PRER)); + rtcinfo(" WUTR: %08x\n", getreg32(STM32_RTC_WUTR)); + rtcinfo(" CALIBR: %08x\n", getreg32(STM32_RTC_CALIBR)); + rtcinfo(" ALRMAR: %08x\n", getreg32(STM32_RTC_ALRMAR)); + rtcinfo(" ALRMBR: %08x\n", getreg32(STM32_RTC_ALRMBR)); + rtcinfo(" SHIFTR: %08x\n", getreg32(STM32_RTC_SHIFTR)); + rtcinfo(" TSTR: %08x\n", getreg32(STM32_RTC_TSTR)); + rtcinfo(" TSDR: %08x\n", getreg32(STM32_RTC_TSDR)); + rtcinfo(" TSSSR: %08x\n", getreg32(STM32_RTC_TSSSR)); + rtcinfo(" CALR: %08x\n", getreg32(STM32_RTC_CALR)); + rtcinfo(" TAFCR: %08x\n", getreg32(STM32_RTC_TAFCR)); + rtcinfo("ALRMASSR: %08x\n", getreg32(STM32_RTC_ALRMASSR)); + rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32_RTC_ALRMBSSR)); + rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG)); +} +#else +# define rtc_dumpregs(msg) +#endif + +/**************************************************************************** + * Name: rtc_dumptime + * + * Description: + * Disable RTC write protection + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_RTC_INFO +static void rtc_dumptime(FAR const struct tm *tp, FAR const char *msg) +{ + rtcinfo("%s:\n", msg); +#if 0 + rtcinfo(" tm_sec: %08x\n", tp->tm_sec); + rtcinfo(" tm_min: %08x\n", tp->tm_min); + rtcinfo(" tm_hour: %08x\n", tp->tm_hour); + rtcinfo(" tm_mday: %08x\n", tp->tm_mday); + rtcinfo(" tm_mon: %08x\n", tp->tm_mon); + rtcinfo(" tm_year: %08x\n", tp->tm_year); +#else + rtcinfo(" tm: %04d-%02d-%02d %02d:%02d:%02d\n", + tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); +#endif +} +#else +# define rtc_dumptime(tp, msg) +#endif + +/**************************************************************************** + * Name: rtc_wprunlock + * + * Description: + * Disable RTC write protection + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void rtc_wprunlock(void) +{ + /* Enable write access to the backup domain. */ + + stm32_pwr_enablebkp(true); + + /* The following steps are required to unlock the write protection on all the + * RTC registers (except for RTC_ISR[13:8], RTC_TAFCR, and RTC_BKPxR). + * + * 1. Write 0xCA into the RTC_WPR register. + * 2. Write 0x53 into the RTC_WPR register. + * + * Writing a wrong key re-activates the write protection. + */ + + putreg32(0xca, STM32_RTC_WPR); + putreg32(0x53, STM32_RTC_WPR); +} + +/**************************************************************************** + * Name: rtc_wprlock + * + * Description: + * Enable RTC write protection + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void rtc_wprlock(void) +{ + /* Writing any wrong key re-activates the write protection. */ + + putreg32(0xff, STM32_RTC_WPR); + + /* Disable write access to the backup domain. */ + + stm32_pwr_enablebkp(false); +} + +/**************************************************************************** + * Name: rtc_synchwait + * + * Description: + * Waits until the RTC Time and Date registers (RTC_TR and RTC_DR) are + * synchronized with RTC APB clock. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +static int rtc_synchwait(void) +{ + volatile uint32_t timeout; + uint32_t regval; + int ret; + + /* Clear Registers synchronization flag (RSF) */ + + regval = getreg32(STM32_RTC_ISR); + regval &= ~RTC_ISR_RSF; + putreg32(regval, STM32_RTC_ISR); + + /* Now wait the registers to become synchronised */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_RSF) != 0) + { + /* Synchronized */ + + ret = OK; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Name: rtc_enterinit + * + * Description: + * Enter RTC initialization mode. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +static int rtc_enterinit(void) +{ + volatile uint32_t timeout; + uint32_t regval; + int ret; + + /* Check if the Initialization mode is already set */ + + regval = getreg32(STM32_RTC_ISR); + + ret = OK; + if ((regval & RTC_ISR_INITF) == 0) + { + /* Set the Initialization mode */ + + putreg32(RTC_ISR_INIT, STM32_RTC_ISR); + + /* Wait until the RTC is in the INIT state (or a timeout occurs) */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_INITF) != 0) + { + ret = OK; + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: rtc_exitinit + * + * Description: + * Exit RTC initialization mode. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +static void rtc_exitinit(void) +{ + uint32_t regval; + + regval = getreg32(STM32_RTC_ISR); + regval &= ~(RTC_ISR_INIT); + putreg32(regval, STM32_RTC_ISR); +} + +/**************************************************************************** + * Name: rtc_bin2bcd + * + * Description: + * Converts a 2 digit binary to BCD format + * + * Input Parameters: + * value - The byte to be converted. + * + * Returned Value: + * The value in BCD representation + * + ****************************************************************************/ + +static uint32_t rtc_bin2bcd(int value) +{ + uint32_t msbcd = 0; + + while (value >= 10) + { + msbcd++; + value -= 10; + } + + return (msbcd << 4) | value; +} + +/**************************************************************************** + * Name: rtc_bin2bcd + * + * Description: + * Convert from 2 digit BCD to binary. + * + * Input Parameters: + * value - The BCD value to be converted. + * + * Returned Value: + * The value in binary representation + * + ****************************************************************************/ + +static int rtc_bcd2bin(uint32_t value) +{ + uint32_t tens = (value >> 4) * 10; + return (int)(tens + (value & 0x0f)); +} + +/**************************************************************************** + * Name: rtc_resume + * + * Description: + * Called when the RTC was already initialized on a previous power cycle. + * This just brings the RTC back into full operation. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +static void rtc_resume(void) +{ +#ifdef CONFIG_RTC_ALARM + uint32_t regval; + + /* Clear the RTC alarm flags */ + + regval = getreg32(STM32_RTC_ISR); + regval &= ~(RTC_ISR_ALRAF | RTC_ISR_ALRBF); + putreg32(regval, STM32_RTC_ISR); + + /* Clear the EXTI Line 18 Pending bit (Connected internally to RTC Alarm) */ + + putreg32(EXTI_RTC_ALARM, STM32_EXTI_PR); +#endif +} + +/**************************************************************************** + * Name: stm32_rtc_alarm_handler + * + * Description: + * RTC ALARM interrupt service routine through the EXTI line + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * context - Architecture specific register save information. + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int stm32_rtc_alarm_handler(int irq, FAR void *context, FAR void *rtc_handler_arg) +{ + FAR struct alm_cbinfo_s *cbinfo; + alm_callback_t cb; + FAR void *arg; + uint32_t isr; + uint32_t cr; + int ret = OK; + + /* Enable write access to the backup domain (RTC registers, RTC + * backup data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(true); + + /* Check for EXTI from Alarm A or B and handle according */ + + cr = getreg32(STM32_RTC_CR); + if ((cr & RTC_CR_ALRAIE) != 0) + { + isr = getreg32(STM32_RTC_ISR); + if ((isr & RTC_ISR_ALRAF) != 0) + { + cbinfo = &g_alarmcb[RTC_ALARMA]; + if (cbinfo->ac_cb != NULL) + { + /* Alarm A callback */ + + cb = cbinfo->ac_cb; + arg = (FAR void *)cbinfo->ac_arg; + + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + + cb(arg, RTC_ALARMA); + } + + /* note, bits 8-13 do /not/ require the write enable procedure */ + + isr = getreg32(STM32_RTC_ISR); + isr &= ~RTC_ISR_ALRAF; + putreg32(isr, STM32_RTC_ISR); + } + } + +#if CONFIG_RTC_NALARMS > 1 + cr = getreg32(STM32_RTC_CR); + if ((cr & RTC_CR_ALRBIE) != 0) + { + isr = getreg32(STM32_RTC_ISR); + if ((isr & RTC_ISR_ALRBF) != 0) + { + cbinfo = &g_alarmcb[RTC_ALARMB]; + if (cbinfo->ac_cb != NULL) + { + /* Alarm B callback */ + + cb = cbinfo->ac_cb; + arg = (FAR void *)cbinfo->ac_arg; + + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + + cb(arg, RTC_ALARMB); + } + + /* note, bits 8-13 do /not/ require the write enable procedure */ + + isr = getreg32(STM32_RTC_ISR); + isr &= ~RTC_ISR_ALRBF; + putreg32(isr, STM32_RTC_ISR); + } + } +#endif + + /* Disable write access to the backup domain (RTC registers, RTC backup + * data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(false); + + return ret; +} +#endif + +/**************************************************************************** + * Name: rtchw_check_alrXwf X= a or B + * + * Description: + * Check registers + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int rtchw_check_alrawf(void) +{ + volatile uint32_t timeout; + uint32_t regval; + int ret = -ETIMEDOUT; + + /* Check RTC_ISR ALRAWF for access to alarm register, + * Can take 2 RTCCLK cycles or timeout + * CubeMX use GetTick. + */ + + for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_ALRAWF) != 0) + { + ret = OK; + break; + } + } + + return ret; +} +#endif + +#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1 +static int rtchw_check_alrbwf(void) +{ + volatile uint32_t timeout; + uint32_t regval; + int ret = -ETIMEDOUT; + + /* Check RTC_ISR ALRBWF for access to alarm register, + * can take 2 RTCCLK cycles or timeout + * CubeMX use GetTick. + */ + + for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_ALRBWF) != 0) + { + ret = OK; + break; + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_rtchw_set_alrmXr X is a or b + * + * Description: + * Set the alarm (A or B) hardware registers, using the required hardware + * access protocol + * + * Input Parameters: + * alarmreg - the register + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg) +{ + int isr; + int ret = -EBUSY; + + /* Need to allow RTC register write + * Disable the write protection for RTC registers + */ + + rtc_wprunlock(); + + /* Disable RTC alarm A & Interrupt A */ + + modifyreg32(STM32_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0); + + /* Ensure Alarm A flag reset; this is edge triggered */ + + isr = getreg32(STM32_RTC_ISR) & ~RTC_ISR_ALRAF; + putreg32(isr, STM32_RTC_ISR); + + /* Wait for Alarm A to be writable */ + + ret = rtchw_check_alrawf(); + if (ret != OK) + { + goto errout_with_wprunlock; + } + + /* Set the RTC Alarm A register */ + + putreg32(alarmreg, STM32_RTC_ALRMAR); + putreg32(0, STM32_RTC_ALRMASSR); + rtcinfo(" ALRMAR: %08x\n", getreg32(STM32_RTC_ALRMAR)); + + /* Enable RTC alarm A */ + + modifyreg32(STM32_RTC_CR, 0, (RTC_CR_ALRAE | RTC_CR_ALRAIE)); + +errout_with_wprunlock: + rtc_wprlock(); + return ret; +} +#endif + +#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1 +static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg) +{ + int isr; + int ret = -EBUSY; + + /* Need to allow RTC register write + * Disable the write protection for RTC registers + */ + + rtc_wprunlock(); + + /* Disable RTC alarm B & Interrupt B */ + + modifyreg32(STM32_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0); + + /* Ensure Alarm B flag reset; this is edge triggered */ + + isr = getreg32(STM32_RTC_ISR) & ~RTC_ISR_ALRBF; + putreg32(isr, STM32_RTC_ISR); + + /* Wait for Alarm B to be writable */ + + ret = rtchw_check_alrbwf(); + if (ret != OK) + { + goto rtchw_set_alrmbr_exit; + } + + /* Set the RTC Alarm B register */ + + putreg32(alarmreg, STM32_RTC_ALRMBR); + putreg32(0, STM32_RTC_ALRMBSSR); + rtcinfo(" ALRMBR: %08x\n", getreg32(STM32_RTC_ALRMBR)); + + /* Enable RTC alarm B */ + + modifyreg32(STM32_RTC_CR, 0, (RTC_CR_ALRBE | RTC_CR_ALRBIE)); + +rtchw_set_alrmbr_exit: + rtc_wprlock(); + return ret; +} +#endif + +/**************************************************************************** + * Name: rtc_enable_alarm + * + * Description: + * Enable ALARM interrupts + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static inline void rtc_enable_alarm(void) +{ + /* Is the alarm already enabled? */ + + if (!g_alarm_enabled) + { + /* Configure RTC interrupt to catch alarm interrupts. All RTC + * interrupts are connected to the EXTI controller. To enable the + * RTC Alarm interrupt, the following sequence is required: + * + * 1. Configure and enable the EXTI Line 18 in interrupt mode and + * select the rising edge sensitivity. + * EXTI line 19 RTC Tamper or Timestamp or CSS_LSE + * EXTI line 20 RTC Wakeup + * 2. Configure and enable the RTC_Alarm IRQ channel in the NVIC. + * 3. Configure the RTC to generate RTC alarms (Alarm A or Alarm B). + */ + + (void)stm32_exti_alarm(true, false, true, stm32_rtc_alarm_handler, NULL); + g_alarm_enabled = true; + } +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_getalarmdatetime + * + * Description: + * Get the current date and time for a RTC alarm. + * + * Input Parameters: + * reg - RTC alarm register + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int stm32_rtc_getalarmdatetime(rtc_alarmreg_t reg, FAR struct tm *tp) +{ + uint32_t data, tmp; + + ASSERT(tp != NULL); + + /* Sample the data time register. */ + + data = getreg32(reg); + + /* Convert the RTC time to fields in struct tm format. All of the STM32 + * ranges of values correspond between struct tm and the time register. + */ + + tmp = (data & (RTC_ALRMR_SU_MASK | RTC_ALRMR_ST_MASK)) >> RTC_ALRMR_SU_SHIFT; + tp->tm_sec = rtc_bcd2bin(tmp); + + tmp = (data & (RTC_ALRMR_MNU_MASK | RTC_ALRMR_MNT_MASK)) >> RTC_ALRMR_MNU_SHIFT; + tp->tm_min = rtc_bcd2bin(tmp); + + tmp = (data & (RTC_ALRMR_HU_MASK | RTC_ALRMR_HT_MASK)) >> RTC_ALRMR_HU_SHIFT; + tp->tm_hour = rtc_bcd2bin(tmp); + + tmp = (data & (RTC_ALRMR_DU_MASK | RTC_ALRMR_DT_MASK)) >> RTC_ALRMR_DU_SHIFT; + tp->tm_mday = rtc_bcd2bin(tmp); + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_rtc_is_initialized + * + * Description: + * Returns 'true' if the RTC has been initialized + * Returns 'false' if the RTC has never been initialized since first time power + * up, and the counters are stopped until it is first initialized. + * + * Input Parameters: + * None + * + * Returned Value: + * Returns true if RTC has been initialized. + * + ****************************************************************************/ + +bool stm32_rtc_is_initialized(void) +{ + uint32_t regval; + + regval = getreg32(RTC_MAGIC_REG); + + return regval == RTC_MAGIC || regval == RTC_MAGIC_TIME_SET; +} + +/**************************************************************************** + * Name: up_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This function is + * called once during the OS initialization sequence + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int up_rtc_initialize(void) +{ + bool init_stat; + uint32_t regval; + + rtc_dumpregs("Before Initialization"); + + /* See if the clock has already been initialized; since it is battery + * backed, we don't need or want to re-initialize on each reset. + */ + + init_stat = stm32_rtc_is_initialized(); + if (!init_stat) + { + /* Enable write access to the backup domain (RTC registers, RTC + * backup data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(true); + +#if defined(CONFIG_RTC_HSECLOCK) + modifyreg32(STM32_RCC_CSR, RCC_CSR_RTCSEL_MASK, RCC_CSR_RTCSEL_HSE); +#elif defined(CONFIG_RTC_LSICLOCK) + modifyreg32(STM32_RCC_CSR, RCC_CSR_RTCSEL_MASK, RCC_CSR_RTCSEL_LSI); +#elif defined(CONFIG_RTC_LSECLOCK) + modifyreg32(STM32_RCC_CSR, RCC_CSR_RTCSEL_MASK, RCC_CSR_RTCSEL_LSE); +#else +# error "No clock for RTC!" +#endif + + /* Enable the RTC Clock by setting the RTCEN bit in the CSR register */ + + modifyreg32(STM32_RCC_CSR, 0, RCC_CSR_RTCEN); + + /* Disable the write protection for RTC registers */ + + rtc_wprunlock(); + + /* Set Initialization mode */ + + if (OK != rtc_enterinit()) + { + /* Enable the write protection for RTC registers */ + + rtc_wprlock(); + + /* Disable write access to the backup domain (RTC registers, RTC backup + * data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(false); + + rtc_dumpregs("After Failed Initialization"); + + return ERROR; + } + else + { + /* Clear RTC_CR FMT, OSEL and POL Bits */ + + regval = getreg32(STM32_RTC_CR); + regval &= ~(RTC_CR_FMT | RTC_CR_OSEL_MASK | RTC_CR_POL); + putreg32(regval, STM32_RTC_CR); + + /* Configure RTC pre-scaler with the required values */ + +#ifdef CONFIG_RTC_HSECLOCK + /* The HSE is divided by 32 prior to the prescaler we set here. + * + * NOTE: max HSE/32 is 4 MHz if it is to be used with RTC + */ + + /* For a 1 MHz clock this yields 0.9999360041 Hz on the second + * timer - which is pretty close. + */ + + putreg32(((uint32_t)7812 << RTC_PRER_PREDIV_S_SHIFT) | + ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT), + STM32_RTC_PRER); +#elif defined(CONFIG_RTC_LSICLOCK) + /* Suitable values for 32.000 KHz LSI clock (29.5 - 34 KHz, though) */ + + putreg32(((uint32_t)0xf9 << RTC_PRER_PREDIV_S_SHIFT) | + ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT), + STM32_RTC_PRER); +#else /* defined(CONFIG_RTC_LSECLOCK) */ + /* Correct values for 32.768 KHz LSE clock */ + + putreg32(((uint32_t)0xff << RTC_PRER_PREDIV_S_SHIFT) | + ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT), + STM32_RTC_PRER); +#endif + + /* Exit Initialization mode */ + + rtc_exitinit(); + + /* Wait for the RTC Time and Date registers to be synchronized with RTC APB + * clock. + */ + + rtc_synchwait(); + + /* Keep the fact that the RTC is initialized */ + + putreg32(RTC_MAGIC, RTC_MAGIC_REG); + + /* Enable the write protection for RTC registers */ + + rtc_wprlock(); + + /* Disable write access to the backup domain (RTC registers, RTC backup + * data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(false); + } + } + else + { + /* Enable write access to the backup domain (RTC registers, RTC + * backup data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(true); + + /* Write protection for RTC registers does not need to be disabled. */ + + rtc_resume(); + + /* Disable write access to the backup domain (RTC registers, RTC backup + * data registers and backup SRAM). + */ + + stm32_pwr_enablebkp(false); + } + + g_rtc_enabled = true; + rtc_dumpregs("After Initialization"); + + return OK; +} + +/************************************************************************************ + * Name: stm32_rtc_irqinitialize + * + * Description: + * Initialize IRQs for RTC, not possible during up_rtc_initialize because + * up_irqinitialize is called later. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int stm32_rtc_irqinitialize(void) +{ + /* Nothing to do */ + + return OK; +} + +/**************************************************************************** + * Name: stm32_rtc_getdatetime_with_subseconds + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected. + * + * Sub-second accuracy is returned through 'nsec'. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * nsec - The location to return the subsecond time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_getdatetime_with_subseconds(FAR struct tm *tp, FAR long *nsec) +{ +#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS + uint32_t ssr; +#endif + uint32_t dr; + uint32_t tr; + uint32_t tmp; + + /* Sample the data time registers. There is a race condition here... If + * we sample the time just before midnight on December 31, the date could + * be wrong because the day rolled over while were sampling. Thus loop for + * checking overflow here is needed. There is a race condition with + * subseconds too. If we sample TR register just before second rolling + * and subseconds are read at wrong second, we get wrong time. + */ + + do + { + dr = getreg32(STM32_RTC_DR); + tr = getreg32(STM32_RTC_TR); +#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS + ssr = getreg32(STM32_RTC_SSR); + tmp = getreg32(STM32_RTC_TR); + if (tmp != tr) + { + continue; + } +#endif + tmp = getreg32(STM32_RTC_DR); + if (tmp == dr) + { + break; + } + } + while (1); + + rtc_dumpregs("Reading Time"); + + /* Convert the RTC time to fields in struct tm format. All of the STM32 + * ranges of values correspond between struct tm and the time register. + */ + + tmp = (tr & (RTC_TR_SU_MASK | RTC_TR_ST_MASK)) >> RTC_TR_SU_SHIFT; + tp->tm_sec = rtc_bcd2bin(tmp); + + tmp = (tr & (RTC_TR_MNU_MASK | RTC_TR_MNT_MASK)) >> RTC_TR_MNU_SHIFT; + tp->tm_min = rtc_bcd2bin(tmp); + + tmp = (tr & (RTC_TR_HU_MASK | RTC_TR_HT_MASK)) >> RTC_TR_HU_SHIFT; + tp->tm_hour = rtc_bcd2bin(tmp); + + /* Now convert the RTC date to fields in struct tm format: + * Days: 1-31 match in both cases. + * Month: STM32 is 1-12, struct tm is 0-11. + * Years: STM32 is 00-99, struct tm is years since 1900. + * WeekDay: STM32 is 1 = Mon - 7 = Sun + * + * Issue: I am not sure what the STM32 years mean. Are these the + * years 2000-2099? I'll assume so. + */ + + tmp = (dr & (RTC_DR_DU_MASK | RTC_DR_DT_MASK)) >> RTC_DR_DU_SHIFT; + tp->tm_mday = rtc_bcd2bin(tmp); + + tmp = (dr & (RTC_DR_MU_MASK | RTC_DR_MT)) >> RTC_DR_MU_SHIFT; + tp->tm_mon = rtc_bcd2bin(tmp) - 1; + + tmp = (dr & (RTC_DR_YU_MASK | RTC_DR_YT_MASK)) >> RTC_DR_YU_SHIFT; + tp->tm_year = rtc_bcd2bin(tmp) + 100; + +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + tmp = (dr & RTC_DR_WDU_MASK) >> RTC_DR_WDU_SHIFT; + tp->tm_wday = tmp % 7; + tp->tm_yday = tp->tm_mday + clock_daysbeforemonth(tp->tm_mon, clock_isleapyear(tp->tm_year + 1900)); + tp->tm_isdst = 0; +#endif + + /* Return RTC sub-seconds if a non-NULL value + * of nsec has been provided to receive the sub-second value. + */ + +#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS + if (nsec) + { + uint32_t prediv_s; + uint32_t usecs; + + prediv_s = getreg32(STM32_RTC_PRER) & RTC_PRER_PREDIV_S_MASK; + prediv_s >>= RTC_PRER_PREDIV_S_SHIFT; + + ssr &= RTC_SSR_MASK; + + /* Maximum prediv_s is 0x7fff, thus we can multiply by 100000 and + * still fit 32-bit unsigned integer. + */ + + usecs = (((prediv_s - ssr) * 100000) / (prediv_s + 1)) * 10; + *nsec = usecs * 1000; + } +#else + DEBUGASSERT(nsec == NULL); +#endif + + rtc_dumptime(tp, "Returning"); + return OK; +} + +/**************************************************************************** + * Name: up_rtc_getdatetime + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected. + * + * NOTE: Some date/time RTC hardware is capability of sub-second accuracy. That + * sub-second accuracy is lost in this interface. However, since the system time + * is reinitialized on each power-up/reset, there will be no timing inaccuracy in + * the long run. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int up_rtc_getdatetime(FAR struct tm *tp) +{ + return stm32_rtc_getdatetime_with_subseconds(tp, NULL); +} + +/**************************************************************************** + * Name: up_rtc_getdatetime_with_subseconds + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected (and CONFIG_RTC_HIRES is not). + * + * NOTE: This interface exposes sub-second accuracy capability of RTC hardware. + * This interface allow maintaining timing accuracy when system time needs constant + * resynchronization with RTC, for example with board level power-save mode utilizing + * deep-sleep modes such as STOP on STM32 MCUs. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * nsec - The location to return the subsecond time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_HAVE_RTC_SUBSECONDS +# ifndef CONFIG_STM32_HAVE_RTC_SUBSECONDS +# error "Invalid config, enable CONFIG_STM32_HAVE_RTC_SUBSECONDS." +# endif +int up_rtc_getdatetime_with_subseconds(FAR struct tm *tp, FAR long *nsec) +{ + return stm32_rtc_getdatetime_with_subseconds(tp, nsec); +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_setdatetime + * + * Description: + * Set the RTC to the provided time. RTC implementations which provide + * up_rtc_getdatetime() (CONFIG_RTC_DATETIME is selected) should provide this + * function. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32_rtc_setdatetime(FAR const struct tm *tp) +{ + uint32_t tr; + uint32_t dr; + int ret; + + rtc_dumptime(tp, "Setting time"); + + /* Then write the broken out values to the RTC */ + + /* Convert the struct tm format to RTC time register fields. + * All of the ranges of values correspond between struct tm and the time + * register. + */ + + tr = (rtc_bin2bcd(tp->tm_sec) << RTC_TR_SU_SHIFT) | + (rtc_bin2bcd(tp->tm_min) << RTC_TR_MNU_SHIFT) | + (rtc_bin2bcd(tp->tm_hour) << RTC_TR_HU_SHIFT); + tr &= ~RTC_TR_RESERVED_BITS; + + /* Now convert the fields in struct tm format to the RTC date register fields: + * Days: 1-31 match in both cases. + * Month: STM32 is 1-12, struct tm is 0-11. + * Years: STM32 is 00-99, struct tm is years since 1900. + * WeekDay: STM32 is 1 = Mon - 7 = Sun + * Issue: I am not sure what the STM32 years mean. Are these the + * years 2000-2099? I'll assume so. + */ + + dr = (rtc_bin2bcd(tp->tm_mday) << RTC_DR_DU_SHIFT) | + ((rtc_bin2bcd(tp->tm_mon + 1)) << RTC_DR_MU_SHIFT) | +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + ((tp->tm_wday == 0 ? 7 : (tp->tm_wday & 7)) << RTC_DR_WDU_SHIFT) | +#endif + ((rtc_bin2bcd(tp->tm_year - 100)) << RTC_DR_YU_SHIFT); + + dr &= ~RTC_DR_RESERVED_BITS; + + /* Disable the write protection for RTC registers */ + + rtc_wprunlock(); + + /* Set Initialization mode */ + + ret = rtc_enterinit(); + if (ret == OK) + { + /* Set the RTC TR and DR registers */ + + putreg32(tr, STM32_RTC_TR); + putreg32(dr, STM32_RTC_DR); + + /* Exit Initialization mode and wait for the RTC Time and Date + * registers to be synchronized with RTC APB clock. + */ + + rtc_exitinit(); + ret = rtc_synchwait(); + } + + /* Remember that the RTC is initialized and had its time set. */ + + if (getreg32(RTC_MAGIC_REG) != RTC_MAGIC_TIME_SET) + { + stm32_pwr_enablebkp(true); + putreg32(RTC_MAGIC_TIME_SET, RTC_MAGIC_REG); + stm32_pwr_enablebkp(false); + } + + /* Re-enable the write protection for RTC registers */ + + rtc_wprlock(); + rtc_dumpregs("New time setting"); + return ret; +} + +/**************************************************************************** + * Name: stm32_rtc_havesettime + * + * Description: + * Check if RTC time has been set. + * + * Returned Value: + * Returns true if RTC date-time have been previously set. + * + ****************************************************************************/ + +bool stm32_rtc_havesettime(void) +{ + return getreg32(RTC_MAGIC_REG) == RTC_MAGIC_TIME_SET; +} + +/**************************************************************************** + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. All RTC implementations must be able + * to set their time based on a standard timespec. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + FAR struct tm newtime; + + /* Break out the time values (not that the time is set only to units of + * seconds) + */ + + (void)gmtime_r(&tp->tv_sec, &newtime); + return stm32_rtc_setdatetime(&newtime); +} + +/**************************************************************************** + * Name: stm32_rtc_setalarm + * + * Description: + * Set an alarm to an absolute time using associated hardware. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int stm32_rtc_setalarm(FAR struct alm_setalarm_s *alminfo) +{ + FAR struct alm_cbinfo_s *cbinfo; + rtc_alarmreg_t alarmreg; + int ret = -EINVAL; + + ASSERT(alminfo != NULL); + DEBUGASSERT(RTC_ALARM_LAST > alminfo->as_id); + + /* Make sure the alarm interrupt is enabled at the NVIC */ + + rtc_enable_alarm(); + + /* REVISIT: Should test that the time is in the future */ + + rtc_dumptime(&alminfo->as_time, "New alarm time"); + + /* Break out the values to the HW alarm register format. The values in + * all STM32 fields match the fields of struct tm in this case. Notice + * that the alarm is limited to one month. + */ + + alarmreg = (rtc_bin2bcd(alminfo->as_time.tm_sec) << RTC_ALRMR_SU_SHIFT) | + (rtc_bin2bcd(alminfo->as_time.tm_min) << RTC_ALRMR_MNU_SHIFT) | + (rtc_bin2bcd(alminfo->as_time.tm_hour) << RTC_ALRMR_HU_SHIFT) | + (rtc_bin2bcd(alminfo->as_time.tm_mday) << RTC_ALRMR_DU_SHIFT); + + /* Set the alarm in hardware and enable interrupts from the RTC */ + + switch (alminfo->as_id) + { + case RTC_ALARMA: + { + cbinfo = &g_alarmcb[RTC_ALARMA]; + cbinfo->ac_cb = alminfo->as_cb; + cbinfo->ac_arg = alminfo->as_arg; + + ret = rtchw_set_alrmar(alarmreg | RTC_ALRMR_ENABLE); + if (ret < 0) + { + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + } + } + break; + +#if CONFIG_RTC_NALARMS > 1 + case RTC_ALARMB: + { + cbinfo = &g_alarmcb[RTC_ALARMB]; + cbinfo->ac_cb = alminfo->as_cb; + cbinfo->ac_arg = alminfo->as_arg; + + ret = rtchw_set_alrmbr(alarmreg | RTC_ALRMR_ENABLE); + if (ret < 0) + { + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + } + } + break; +#endif + + default: + rtcerr("ERROR: Invalid ALARM%d\n", alminfo->as_id); + break; + } + + rtc_dumpregs("After alarm setting"); + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_cancelalarm + * + * Description: + * Cancel an alarm. + * + * Input Parameters: + * alarmid - Identifies the alarm to be cancelled + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int stm32_rtc_cancelalarm(enum alm_id_e alarmid) +{ + int ret = -EINVAL; + + DEBUGASSERT(RTC_ALARM_LAST > alarmid); + + /* Cancel the alarm in hardware and disable interrupts */ + + switch (alarmid) + { + case RTC_ALARMA: + { + /* Cancel the global callback function */ + + g_alarmcb[alarmid].ac_cb = NULL; + g_alarmcb[alarmid].ac_arg = NULL; + + /* Need to follow RTC register wrote protection. + * Disable the write protection for RTC registers + */ + + rtc_wprunlock(); + + /* Disable RTC alarm and interrupt */ + + modifyreg32(STM32_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0); + + ret = rtchw_check_alrawf(); + if (ret < 0) + { + goto errout_with_wprunlock; + } + + /* Unset the alarm */ + + putreg32(-1, STM32_RTC_ALRMAR); + modifyreg32(STM32_RTC_ISR, RTC_ISR_ALRAF, 0); + rtc_wprlock(); + ret = OK; + } + break; + +#if CONFIG_RTC_NALARMS > 1 + case RTC_ALARMB: + { + /* Cancel the global callback function */ + + g_alarmcb[alarmid].ac_cb = NULL; + g_alarmcb[alarmid].ac_arg = NULL; + + /* Need to follow RTC register wrote protection. + * Disable the write protection for RTC registers + */ + + rtc_wprunlock(); + + /* Disable RTC alarm and interrupt */ + + modifyreg32(STM32_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0); + + ret = rtchw_check_alrbwf(); + if (ret < 0) + { + goto errout_with_wprunlock; + } + + /* Unset the alarm */ + + putreg32(-1, STM32_RTC_ALRMBR); + modifyreg32(STM32_RTC_ISR, RTC_ISR_ALRBF, 0); + rtc_wprlock(); + ret = OK; + } + break; +#endif + + default: + rtcerr("ERROR: Invalid ALARM%d\n", alarmid); + break; + } + + return ret; + +errout_with_wprunlock: + rtc_wprlock(); + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_rdalarm + * + * Description: + * Query an alarm configured in hardware. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int stm32_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo) +{ + rtc_alarmreg_t alarmreg; + int ret = -EINVAL; + + ASSERT(alminfo != NULL); + DEBUGASSERT(RTC_ALARM_LAST > alminfo->ar_id); + + switch (alminfo->ar_id) + { + case RTC_ALARMA: + { + alarmreg = STM32_RTC_ALRMAR; + ret = stm32_rtc_getalarmdatetime(alarmreg, + (struct tm *)alminfo->ar_time); + } + break; + +#if CONFIG_RTC_NALARMS > 1 + case RTC_ALARMB: + { + alarmreg = STM32_RTC_ALRMBR; + ret = stm32_rtc_getalarmdatetime(alarmreg, + (struct tm *)alminfo->ar_time); + } + break; +#endif + + default: + rtcerr("ERROR: Invalid ALARM%d\n", alminfo->ar_id); + break; + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_wakeup_handler + * + * Description: + * RTC WAKEUP interrupt service routine through the EXTI line + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32_rtc_wakeup_handler(int irq, FAR void *context, FAR void *arg) +{ + uint32_t regval = 0; + + stm32_pwr_enablebkp(true); + + regval = getreg32(STM32_RTC_ISR); + regval &= ~RTC_ISR_WUTF; + putreg32(regval, STM32_RTC_ISR); + + stm32_pwr_enablebkp(false); + + if (g_wakeupcb != NULL) + { + g_wakeupcb(); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: rtc_enable_wakeup + * + * Description: + * Enable periodic wakeup interrupts + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static inline void rtc_enable_wakeup(void) +{ + if (!g_wakeup_enabled) + { + (void)stm32_exti_wakeup(true, false, true, stm32_rtc_wakeup_handler, NULL); + g_wakeup_enabled = true; + } +} +#endif + +/************************************************************************************ + * Name: rtc_set_wcksel + * + * Description: + * Sets RTC wakeup clock selection value + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static inline void rtc_set_wcksel(unsigned int wucksel) +{ + uint32_t regval = 0; + + regval = getreg32(STM32_RTC_CR); + regval &= ~RTC_CR_WUCKSEL_MASK; + regval |= wucksel; + putreg32(regval, STM32_RTC_CR); +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_setperiodic + * + * Description: + * Set a periodic RTC wakeup + * + * Input Parameters: + * period - Time to sleep between wakeups + * callback - Function to call when the period expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +int stm32_rtc_setperiodic(FAR const struct timespec *period, wakeupcb_t callback) +{ + unsigned int wutr_val; + int ret; + int timeout; + uint32_t regval; + uint32_t secs; + uint32_t millisecs; + +#if defined(CONFIG_RTC_HSECLOCK) +# error "Periodic wakeup not available for HSE" +#elif defined(CONFIG_RTC_LSICLOCK) +# error "Periodic wakeup not available for LSI (and it is too inaccurate!)" +#elif defined(CONFIG_RTC_LSECLOCK) + const uint32_t rtc_div16_max_msecs = 16 * 1000 * 0xffffU / STM32_LSE_FREQUENCY; +#else +# error "No clock for RTC!" +#endif + + /* Lets use RTC wake-up with 0.001 sec to ~18 hour range. + * + * TODO: scale to higher periods, with necessary losing some precision. + * We currently go for subseconds accuracy instead of maximum period. + */ + + if (period->tv_sec > 0xffffU || + (period->tv_sec == 0xffffU && period->tv_nsec > 0)) + { + /* More than max. */ + + secs = 0xffffU; + millisecs = secs * 1000; + } + else + { + secs = period->tv_sec; + millisecs = secs * 1000 + period->tv_nsec / NSEC_PER_MSEC; + } + + if (millisecs == 0) + { + return -EINVAL; + } + + /* Make sure the alarm interrupt is enabled at the NVIC */ + + rtc_enable_wakeup(); + + rtc_wprunlock(); + + /* Clear WUTE in RTC_CR to disable the wakeup timer */ + + regval = getreg32(STM32_RTC_CR); + regval &= ~RTC_CR_WUTE; + putreg32(regval, STM32_RTC_CR); + + /* Poll WUTWF until it is set in RTC_ISR (takes around 2 RTCCLK clock cycles) */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_WUTWF) != 0) + { + /* Synchronized */ + + ret = OK; + break; + } + } + + /* Set callback function pointer. */ + + g_wakeupcb = callback; + + if (millisecs <= rtc_div16_max_msecs) + { + unsigned int ticks; + + /* Select wake-up with 32768/16 hz counter. */ + + rtc_set_wcksel(RTC_CR_WUCKSEL_RTCDIV16); + + /* Get number of ticks. */ + + ticks = millisecs * STM32_LSE_FREQUENCY / (16 * 1000); + + /* Wake-up is after WUT+1 ticks. */ + + wutr_val = ticks - 1; + } + else + { + /* Select wake-up with 1hz counter. */ + + rtc_set_wcksel(RTC_CR_WUCKSEL_CKSPRE); + + /* Wake-up is after WUT+1 ticks. */ + + wutr_val = secs - 1; + } + + /* Program the wakeup auto-reload value WUT[15:0], and the wakeup clock + * selection. + */ + + putreg32(wutr_val, STM32_RTC_WUTR); + + regval = getreg32(STM32_RTC_CR); + regval |= RTC_CR_WUTIE | RTC_CR_WUTE; + putreg32(regval, STM32_RTC_CR); + + /* Just in case resets the WUTF flag in RTC_ISR */ + + regval = getreg32(STM32_RTC_ISR); + regval &= ~RTC_ISR_WUTF; + putreg32(regval, STM32_RTC_ISR); + + rtc_wprlock(); + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_rtc_cancelperiodic + * + * Description: + * Cancel a periodic wakeup + * + * Input Parameters: + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ +#ifdef CONFIG_RTC_PERIODIC +int stm32_rtc_cancelperiodic(void) +{ + int ret = OK; + int timeout = 0; + uint32_t regval = 0; + + rtc_wprunlock(); + + /* Clear WUTE and WUTIE in RTC_CR to disable the wakeup timer */ + + regval = getreg32(STM32_RTC_CR); + regval &= ~(RTC_CR_WUTE | RTC_CR_WUTIE); + putreg32(regval, STM32_RTC_CR); + + /* Poll WUTWF until it is set in RTC_ISR (takes around 2 RTCCLK clock cycles) */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++) + { + regval = getreg32(STM32_RTC_ISR); + if ((regval & RTC_ISR_WUTWF) != 0) + { + /* Synchronized */ + + ret = OK; + break; + } + } + + /* Clears RTC_WUTR register */ + + regval = getreg32(STM32_RTC_WUTR); + regval &= ~RTC_WUTR_MASK; + putreg32(regval, STM32_RTC_WUTR); + + rtc_wprlock(); + + return ret; +} +#endif + +#endif /* CONFIG_RTC */ diff --git a/arch/arm/src/stm32l4/Make.defs b/arch/arm/src/stm32l4/Make.defs index c5ced697e9..c073ad1a2b 100644 --- a/arch/arm/src/stm32l4/Make.defs +++ b/arch/arm/src/stm32l4/Make.defs @@ -182,6 +182,9 @@ ifeq ($(CONFIG_RTC),y) ifeq ($(CONFIG_RTC_ALARM),y) CHIP_CSRCS += stm32l4_exti_alarm.c endif +ifeq ($(CONFIG_RTC_PERIODIC),y) +CHIP_CSRCS += stm32l4_exti_wakeup.c +endif ifeq ($(CONFIG_RTC_DRIVER),y) CHIP_CSRCS += stm32l4_rtc_lowerhalf.c CHIP_CSRCS += stm32l4_rtc.c diff --git a/arch/arm/src/stm32l4/stm32l4_adc.c b/arch/arm/src/stm32l4/stm32l4_adc.c index a689587dda..56a9ae81d7 100644 --- a/arch/arm/src/stm32l4/stm32l4_adc.c +++ b/arch/arm/src/stm32l4/stm32l4_adc.c @@ -1016,7 +1016,7 @@ static int adc_timinit(FAR struct stm32_dev_s *priv) * states. Check if ADC is in progress and if so prevent from entering STOP. * ****************************************************************************/ -#if CONFIG_PM +#ifdef CONFIG_PM static int adc_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e state) { diff --git a/arch/arm/src/stm32l4/stm32l4_exti.h b/arch/arm/src/stm32l4/stm32l4_exti.h index 2f57229ea2..14d2e7100a 100644 --- a/arch/arm/src/stm32l4/stm32l4_exti.h +++ b/arch/arm/src/stm32l4/stm32l4_exti.h @@ -112,6 +112,29 @@ int stm32l4_exti_alarm(bool risingedge, bool fallingedge, bool event, xcpt_t func, void *arg); #endif +/**************************************************************************** + * Name: stm32l4_exti_wakeup + * + * Description: + * Sets/clears EXTI wakeup interrupt. + * + * Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returns: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +int stm32l4_exti_wakeup(bool risingedge, bool fallingedge, bool event, + xcpt_t func, void *arg); +#endif + /**************************************************************************** * Name: stm32l4_exti_comp * @@ -120,7 +143,7 @@ int stm32l4_exti_alarm(bool risingedge, bool fallingedge, bool event, * * Parameters: * - cmp: comparator - * - rising/falling edge: enables interrupt on rising/falling edget + * - rising/falling edge: enables interrupt on rising/falling edges * - event: generate event when set * - func: when non-NULL, generate interrupt * - arg: Argument passed to the interrupt callback diff --git a/arch/arm/src/stm32l4/stm32l4_exti_wakeup.c b/arch/arm/src/stm32l4/stm32l4_exti_wakeup.c new file mode 100644 index 0000000000..195cf2c1ac --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_exti_wakeup.c @@ -0,0 +1,153 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_exti_wakeup.c + * + * Copyright (C) 2009, 2012, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32l4_gpio.h" +#include "stm32l4_exti.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Interrupt handlers attached to the RTC WAKEUP EXTI */ + +static xcpt_t g_wakeup_callback; +static void *g_callback_arg; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_exti_wakeup_isr + * + * Description: + * EXTI periodic WAKEUP interrupt service routine/dispatcher + * + ****************************************************************************/ + +static int stm32l4_exti_wakeup_isr(int irq, void *context, FAR void *arg) +{ + int ret = OK; + + /* Dispatch the interrupt to the handler */ + + if (g_wakeup_callback != NULL) + { + ret = g_wakeup_callback(irq, context, g_callback_arg); + } + + /* Clear the pending EXTI interrupt */ + + putreg32(EXTI1_RTC_WAKEUP, STM32L4_EXTI1_PR); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_exti_wakeup + * + * Description: + * Sets/clears EXTI wakeup interrupt. + * + * Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * + * Returns: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +int stm32l4_exti_wakeup(bool risingedge, bool fallingedge, bool event, + xcpt_t func, void *arg) +{ + g_wakeup_callback = func; + g_callback_arg = arg; + + /* Install external interrupt handlers (if not already attached) */ + + if (func) + { + irq_attach(STM32L4_IRQ_RTC_WKUP, stm32l4_exti_wakeup_isr, NULL); + up_enable_irq(STM32L4_IRQ_RTC_WKUP); + } + else + { + up_disable_irq(STM32L4_IRQ_RTC_WKUP); + } + + /* Configure rising/falling edges */ + + modifyreg32(STM32L4_EXTI1_RTSR, + risingedge ? 0 : EXTI1_RTC_WAKEUP, + risingedge ? EXTI1_RTC_WAKEUP : 0); + modifyreg32(STM32L4_EXTI1_FTSR, + fallingedge ? 0 : EXTI1_RTC_WAKEUP, + fallingedge ? EXTI1_RTC_WAKEUP : 0); + + /* Enable Events and Interrupts */ + + modifyreg32(STM32L4_EXTI1_EMR, + event ? 0 : EXTI1_RTC_WAKEUP, + event ? EXTI1_RTC_WAKEUP : 0); + modifyreg32(STM32L4_EXTI1_IMR, + func ? 0 : EXTI1_RTC_WAKEUP, + func ? EXTI1_RTC_WAKEUP : 0); + + return OK; +} diff --git a/arch/arm/src/stm32l4/stm32l4_rtc.c b/arch/arm/src/stm32l4/stm32l4_rtc.c index 09f2602dba..7707d264c5 100644 --- a/arch/arm/src/stm32l4/stm32l4_rtc.c +++ b/arch/arm/src/stm32l4/stm32l4_rtc.c @@ -4,6 +4,7 @@ * Copyright (C) 2012-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * dev@ziggurat29.com (adaptations to stm32l4) + * Juha Niskanen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -131,6 +132,11 @@ static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST]; static bool g_alarm_enabled; /* True: Alarm interrupts are enabled */ #endif +#ifdef CONFIG_RTC_PERIODIC +static wakeupcb_t g_wakeupcb; +static bool g_wakeup_enabled; /* True: Wakeup interrupts are enabled */ +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -1601,4 +1607,273 @@ int stm32l4_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo) } #endif +/**************************************************************************** + * Name: stm32l4_rtc_wakeup_handler + * + * Description: + * RTC WAKEUP interrupt service routine through the EXTI line + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32l4_rtc_wakeup_handler(int irq, FAR void *context, FAR void *arg) +{ + uint32_t regval = 0; + + (void)stm32l4_pwr_enablebkp(true); + + regval = getreg32(STM32L4_RTC_ISR); + regval &= ~RTC_ISR_WUTF; + putreg32(regval, STM32L4_RTC_ISR); + + (void)stm32l4_pwr_enablebkp(false); + + if (g_wakeupcb != NULL) + { + g_wakeupcb(); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: rtc_enable_wakeup + * + * Description: + * Enable periodic wakeup interrupts + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static inline void rtc_enable_wakeup(void) +{ + if (!g_wakeup_enabled) + { + (void)stm32l4_exti_wakeup(true, false, true, stm32l4_rtc_wakeup_handler, NULL); + g_wakeup_enabled = true; + } +} +#endif + +/************************************************************************************ + * Name: rtc_set_wcksel + * + * Description: + * Sets RTC wakeup clock selection value + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static inline void rtc_set_wcksel(unsigned int wucksel) +{ + uint32_t regval = 0; + + regval = getreg32(STM32L4_RTC_CR); + regval &= ~RTC_CR_WUCKSEL_MASK; + regval |= wucksel; + putreg32(regval, STM32L4_RTC_CR); +} +#endif + +/**************************************************************************** + * Name: stm32l4_rtc_setperiodic + * + * Description: + * Set a periodic RTC wakeup + * + * Input Parameters: + * period - Time to sleep between wakeups + * callback - Function to call when the period expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +int stm32l4_rtc_setperiodic(FAR const struct timespec *period, wakeupcb_t callback) +{ + unsigned int wutr_val; + int ret; + int timeout; + uint32_t regval; + uint32_t secs; + uint32_t millisecs; + +#if defined(CONFIG_STM32L4_RTC_HSECLOCK) +# error "Periodic wakeup not available for HSE" +#elif defined(CONFIG_STM32L4_RTC_LSICLOCK) +# error "Periodic wakeup not available for LSI (and it is too inaccurate!)" +#elif defined(CONFIG_STM32L4_RTC_LSECLOCK) + const uint32_t rtc_div16_max_msecs = 16 * 1000 * 0xffffU / STM32L4_LSE_FREQUENCY; +#else +# error "No clock for RTC!" +#endif + + /* Lets use RTC wake-up with 0.001 sec to ~18 hour range. + * + * TODO: scale to higher periods, with necessary losing some precision. + * We currently go for subseconds accuracy instead of maximum period. + */ + + if (period->tv_sec > 0xffffU || + (period->tv_sec == 0xffffU && period->tv_nsec > 0)) + { + /* More than max. */ + + secs = 0xffffU; + millisecs = secs * 1000; + } + else + { + secs = period->tv_sec; + millisecs = secs * 1000 + period->tv_nsec / NSEC_PER_MSEC; + } + + if (millisecs == 0) + { + return -EINVAL; + } + + /* Make sure the alarm interrupt is enabled at the NVIC */ + + rtc_enable_wakeup(); + + rtc_wprunlock(); + + /* Clear WUTE in RTC_CR to disable the wakeup timer */ + + regval = getreg32(STM32L4_RTC_CR); + regval &= ~RTC_CR_WUTE; + putreg32(regval, STM32L4_RTC_CR); + + /* Poll WUTWF until it is set in RTC_ISR (takes around 2 RTCCLK clock cycles) */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++) + { + regval = getreg32(STM32L4_RTC_ISR); + if ((regval & RTC_ISR_WUTWF) != 0) + { + /* Synchronized */ + + ret = OK; + break; + } + } + + /* Set callback function pointer. */ + + g_wakeupcb = callback; + + if (millisecs <= rtc_div16_max_msecs) + { + unsigned int ticks; + + /* Select wake-up with 32768/16 hz counter. */ + + rtc_set_wcksel(RTC_CR_WUCKSEL_RTCDIV16); + + /* Get number of ticks. */ + + ticks = millisecs * STM32L4_LSE_FREQUENCY / (16 * 1000); + + /* Wake-up is after WUT+1 ticks. */ + + wutr_val = ticks - 1; + } + else + { + /* Select wake-up with 1hz counter. */ + + rtc_set_wcksel(RTC_CR_WUCKSEL_CKSPRE); + + /* Wake-up is after WUT+1 ticks. */ + + wutr_val = secs - 1; + } + + /* Program the wakeup auto-reload value WUT[15:0], and the wakeup clock + * selection. + */ + + putreg32(wutr_val, STM32L4_RTC_WUTR); + + regval = getreg32(STM32L4_RTC_CR); + regval |= RTC_CR_WUTIE | RTC_CR_WUTE; + putreg32(regval, STM32L4_RTC_CR); + + /* Just in case resets the WUTF flag in RTC_ISR */ + + regval = getreg32(STM32L4_RTC_ISR); + regval &= ~RTC_ISR_WUTF; + putreg32(regval, STM32L4_RTC_ISR); + + rtc_wprlock(); + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32l4_rtc_cancelperiodic + * + * Description: + * Cancel a periodic wakeup + * + * Input Parameters: + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ +#ifdef CONFIG_RTC_PERIODIC +int stm32l4_rtc_cancelperiodic(void) +{ + int ret = OK; + int timeout = 0; + uint32_t regval = 0; + + rtc_wprunlock(); + + /* Clear WUTE and WUTIE in RTC_CR to disable the wakeup timer */ + + regval = getreg32(STM32L4_RTC_CR); + regval &= ~(RTC_CR_WUTE | RTC_CR_WUTIE); + putreg32(regval, STM32L4_RTC_CR); + + /* Poll WUTWF until it is set in RTC_ISR (takes around 2 RTCCLK clock cycles) */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++) + { + regval = getreg32(STM32L4_RTC_ISR); + if ((regval & RTC_ISR_WUTWF) != 0) + { + /* Synchronized */ + + ret = OK; + break; + } + } + + /* Clears RTC_WUTR register */ + + regval = getreg32(STM32L4_RTC_WUTR); + regval &= ~RTC_WUTR_MASK; + putreg32(regval, STM32L4_RTC_WUTR); + + rtc_wprlock(); + + return ret; +} +#endif + #endif /* CONFIG_RTC */ diff --git a/arch/arm/src/stm32l4/stm32l4_rtc.h b/arch/arm/src/stm32l4/stm32l4_rtc.h index e1d6aa7883..c7da87e212 100644 --- a/arch/arm/src/stm32l4/stm32l4_rtc.h +++ b/arch/arm/src/stm32l4/stm32l4_rtc.h @@ -96,6 +96,10 @@ struct alm_rdalarm_s #endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_PERIODIC +typedef CODE int (*wakeupcb_t)(void); +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -195,7 +199,7 @@ bool stm32l4_rtc_havesettime(void); * Name: stm32l4_rtc_setalarm * * Description: - * Set an alarm to an asbolute time using associated hardware. + * Set an alarm to an absolute time using associated hardware. * * Input Parameters: * alminfo - Information about the alarm configuration. @@ -227,7 +231,7 @@ int stm32l4_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo); * Name: stm32l4_rtc_cancelalarm * * Description: - * Cancel an alaram. + * Cancel an alarm. * * Input Parameters: * alarmid - Identifies the alarm to be cancelled @@ -240,6 +244,41 @@ int stm32l4_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo); int stm32l4_rtc_cancelalarm(enum alm_id_e alarmid); #endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_PERIODIC + +/**************************************************************************** + * Name: stm32l4_rtc_setperiodic + * + * Description: + * Set a periodic RTC wakeup + * + * Input Parameters: + * period - Time to sleep between wakeups + * callback - Function to call when the period expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32l4_rtc_setperiodic(FAR const struct timespec *period, wakeupcb_t callback); + +/**************************************************************************** + * Name: stm32l4_rtc_cancelperiodic + * + * Description: + * Cancel a periodic wakeup + * + * Input Parameters: + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int stm32l4_rtc_cancelperiodic(void); +#endif /* CONFIG_RTC_PERIODIC */ + /**************************************************************************** * Name: stm32l4_rtc_lowerhalf * diff --git a/arch/arm/src/stm32l4/stm32l4_rtc_lowerhalf.c b/arch/arm/src/stm32l4/stm32l4_rtc_lowerhalf.c index 69a357b991..b49c8f1f4b 100644 --- a/arch/arm/src/stm32l4/stm32l4_rtc_lowerhalf.c +++ b/arch/arm/src/stm32l4/stm32l4_rtc_lowerhalf.c @@ -67,7 +67,7 @@ struct stm32l4_cbinfo_s { volatile rtc_alarm_callback_t cb; /* Callback when the alarm expires */ - volatile FAR void *priv; /* Private argurment to accompany callback */ + volatile FAR void *priv; /* Private argument to accompany callback */ uint8_t id; /* Identifies the alarm */ }; #endif @@ -95,6 +95,12 @@ struct stm32l4_lowerhalf_s struct stm32l4_cbinfo_s cbinfo[STM32L4_NALARMS]; #endif + +#ifdef CONFIG_RTC_PERIODIC + /* Periodic wakeup information */ + + struct lower_setperiodic_s periodic; +#endif }; /**************************************************************************** @@ -111,13 +117,19 @@ static bool stm32l4_havesettime(FAR struct rtc_lowerhalf_s *lower); #ifdef CONFIG_RTC_ALARM static int stm32l4_setalarm(FAR struct rtc_lowerhalf_s *lower, - FAR const struct lower_setalarm_s *alarminfo); + FAR const struct lower_setalarm_s *alarminfo); static int stm32l4_setrelative(FAR struct rtc_lowerhalf_s *lower, - FAR const struct lower_setrelative_s *alarminfo); + FAR const struct lower_setrelative_s *alarminfo); static int stm32l4_cancelalarm(FAR struct rtc_lowerhalf_s *lower, - int alarmid); + int alarmid); static int stm32l4_rdalarm(FAR struct rtc_lowerhalf_s *lower, - FAR struct lower_rdalarm_s *alarminfo); + FAR struct lower_rdalarm_s *alarminfo); +#endif + +#ifdef CONFIG_RTC_PERIODIC +static int stm32l4_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo); +static int stm32l4_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id); #endif /**************************************************************************** @@ -137,6 +149,10 @@ static const struct rtc_ops_s g_rtc_ops = .cancelalarm = stm32l4_cancelalarm, .rdalarm = stm32l4_rdalarm, #endif +#ifdef CONFIG_RTC_PERIODIC + .setperiodic = stm32l4_setperiodic, + .cancelperiodic = stm32l4_cancelperiodic, +#endif #ifdef CONFIG_RTC_IOCTL .ioctl = NULL, #endif @@ -271,11 +287,11 @@ static int stm32l4_settime(FAR struct rtc_lowerhalf_s *lower, ret = nxsem_wait(&priv->devsem); if (ret < 0) - { - return ret; - } + { + return ret; + } - /* This operation depends on the fact that struct rtc_time is cast + /* This operation depends on the fact that struct rtc_time is cast * compatible with struct tm. */ @@ -339,9 +355,9 @@ static int stm32l4_setalarm(FAR struct rtc_lowerhalf_s *lower, ret = nxsem_wait(&priv->devsem); if (ret < 0) - { - return ret; - } + { + return ret; + } ret = -EINVAL; if (alarminfo->id == RTC_ALARMA || alarminfo->id == RTC_ALARMB) @@ -479,9 +495,9 @@ static int stm32l4_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) ret = nxsem_wait(&priv->devsem); if (ret < 0) - { - return ret; - } + { + return ret; + } /* ID0-> Alarm A; ID1 -> Alarm B */ @@ -551,6 +567,131 @@ static int stm32l4_rdalarm(FAR struct rtc_lowerhalf_s *lower, } #endif +/**************************************************************************** + * Name: stm32l4_periodic_callback + * + * Description: + * This is the function that is called from the RTC driver when the periodic + * wakeup goes off. It just invokes the upper half drivers callback. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32l4_periodic_callback(void) +{ + FAR struct stm32l4_lowerhalf_s *lower; + struct lower_setperiodic_s *cbinfo; + rtc_wakeup_callback_t cb; + FAR void *priv; + + lower = (FAR struct stm32l4_lowerhalf_s *)&g_rtc_lowerhalf; + + cbinfo = &lower->periodic; + cb = (rtc_wakeup_callback_t)cbinfo->cb; + priv = (FAR void *)cbinfo->priv; + + /* Perform the callback */ + + if (cb != NULL) + { + cb(priv, 0); + } + + return OK; +} +#endif /* CONFIG_RTC_PERIODIC */ + +/**************************************************************************** + * Name: stm32l4_setperiodic + * + * Description: + * Set a new periodic wakeup relative to the current time, with a given + * period. This function implements the setperiodic() method of the RTC + * driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * alarminfo - Provided information needed to set the wakeup activity + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32l4_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo) +{ + FAR struct stm32l4_lowerhalf_s *priv; + int ret; + + ASSERT(lower != NULL && alarminfo != NULL); + priv = (FAR struct stm32l4_lowerhalf_s *)lower; + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + memcpy(&priv->periodic, alarminfo, sizeof(struct lower_setperiodic_s)); + + ret = stm32l4_rtc_setperiodic(&alarminfo->period, stm32l4_periodic_callback); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32l4_cancelperiodic + * + * Description: + * Cancel the current periodic wakeup activity. This function implements + * the cancelperiodic() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int stm32l4_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id) +{ + FAR struct stm32l4_lowerhalf_s *priv; + int ret; + + DEBUGASSERT(lower != NULL); + priv = (FAR struct stm32l4_lowerhalf_s *)lower; + + DEBUGASSERT(id == 0); + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = stm32l4_rtc_cancelperiodic(); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/drivers/timers/rtc.c b/drivers/timers/rtc.c index ac89e7d089..f38979ee50 100644 --- a/drivers/timers/rtc.c +++ b/drivers/timers/rtc.c @@ -54,7 +54,7 @@ * Private Types ****************************************************************************/ -#ifdef CONFIG_RTC_ALARM +#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_PERIODIC) struct rtc_alarminfo_s { bool active; /* True: alarm is active */ @@ -80,6 +80,12 @@ struct rtc_upperhalf_s struct rtc_alarminfo_s alarminfo[CONFIG_RTC_NALARMS]; #endif + +#ifdef CONFIG_RTC_PERIODIC + /* Currently only one periodic wakeup is supported. */ + + struct rtc_alarminfo_s periodicinfo; +#endif }; /**************************************************************************** @@ -96,6 +102,10 @@ static void rtc_destroy(FAR struct rtc_upperhalf_s *upper); static void rtc_alarm_callback(FAR void *priv, int id); #endif +#ifdef CONFIG_RTC_PERIODIC +static void rtc_periodic_callback(FAR void *priv, int id); +#endif + /* Character driver methods */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS @@ -203,6 +213,43 @@ static void rtc_alarm_callback(FAR void *priv, int alarmid) } #endif +/**************************************************************************** + * Name: rtc_periodic_callback + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static void rtc_periodic_callback(FAR void *priv, int alarmid) +{ + FAR struct rtc_upperhalf_s *upper = (FAR struct rtc_upperhalf_s *)priv; + FAR struct rtc_alarminfo_s *alarminfo; + + DEBUGASSERT(upper != NULL && alarmid >= 0); + alarminfo = &upper->periodicinfo; + + /* Do we think that the alarm is active? It might be due to some + * race condition between a cancellation event and the alarm + * expiration. + */ + + if (alarminfo->active) + { + /* Yes.. signal the alarm expiration */ + +#ifdef CONFIG_CAN_PASS_STRUCTS + (void)nxsig_queue(alarminfo->pid, alarminfo->signo, + alarminfo->sigvalue); +#else + (void)nxsig_queue(alarminfo->pid, alarminfo->signo, + alarminfo->sigvalue->sival_ptr); +#endif + } + + /* The alarm is no longer active */ + + alarminfo->active = false; +} +#endif + /**************************************************************************** * Name: rtc_open ****************************************************************************/ @@ -573,6 +620,105 @@ static int rtc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; #endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_PERIODIC + /* RTC_SET_PERIODIC set a periodic wakeup. + * + * Argument: A read-only reference to a struct rtc_setperiodic_s containing the + * new wakeup period to be set. + */ + + case RTC_SET_PERIODIC: + { + FAR const struct rtc_setperiodic_s *alarminfo = + (FAR const struct rtc_setperiodic_s *)((uintptr_t)arg); + FAR struct rtc_alarminfo_s *upperinfo; + struct lower_setperiodic_s lowerinfo; + int id; + + DEBUGASSERT(alarminfo != NULL); + id = alarminfo->id; + DEBUGASSERT(id >= 0); + + /* Is the alarm active? */ + + upperinfo = &upper->periodicinfo; + if (upperinfo->active) + { + /* Yes, cancel it */ + + if (ops->cancelperiodic) + { + (void)ops->cancelperiodic(upper->lower, id); + } + + upperinfo->active = false; + } + + if (ops->setperiodic) + { + pid_t pid; + + /* A PID of zero means to signal the calling task */ + + pid = alarminfo->pid; + if (pid == 0) + { + pid = getpid(); + } + + /* Save the signal info to be used to notify the caller when the + * alarm expires. + */ + + upperinfo->active = true; + upperinfo->signo = alarminfo->signo; + upperinfo->pid = pid; + upperinfo->sigvalue = alarminfo->sigvalue; + + /* Format the alarm info needed by the lower half driver. */ + + lowerinfo.id = id; + lowerinfo.cb = rtc_periodic_callback; + lowerinfo.priv = (FAR void *)upper; + lowerinfo.period = alarminfo->period; + + /* Then set the periodic wakeup. */ + + ret = ops->setperiodic(upper->lower, &lowerinfo); + if (ret < 0) + { + upperinfo->active = false; + } + } + } + break; + + /* RTC_CANCEL_PERIODIC cancel the periodic wakeup. + * + * Argument: An ID value that indicates which wakeup should be canceled. + */ + + case RTC_CANCEL_PERIODIC: + { + FAR struct rtc_alarminfo_s *upperinfo; + int id = (int)arg; + + DEBUGASSERT(id >= 0); + + upperinfo = &upper->periodicinfo; + + if (ops->cancelperiodic) + { + ret = ops->cancelperiodic(upper->lower, id); + if (ret == OK) + { + upperinfo->active = false; + } + } + } + break; +#endif /* CONFIG_RTC_PERIODIC */ + /* Forward any unrecognized IOCTLs to the lower half driver... they * may represent some architecture-specific command. */ diff --git a/include/nuttx/timers/rtc.h b/include/nuttx/timers/rtc.h index c0b6e7ff32..b5c1a6bf46 100644 --- a/include/nuttx/timers/rtc.h +++ b/include/nuttx/timers/rtc.h @@ -51,6 +51,7 @@ #include #include #include +#include #include @@ -90,6 +91,10 @@ * CONFIG_RTC_ALARM - Enable if the RTC hardware supports setting of an * alarm. A callback function will be executed when the alarm goes off * + * CONFIG_RTC_PERIODIC - Enable if the RTC hardware supports setting a + * periodic wakeup. A callback function will be executed when the wakeup happens. + * This is an experimental feature. + * * CONFIG_RTC_DRIVER - Enable building the upper-half RTC driver */ @@ -170,7 +175,7 @@ #define RTC_SET_RELATIVE _RTCIOC(0x0005) -/* RTC_SET_RELATIVE cancel the alarm. +/* RTC_CANCEL_ALARM cancel the alarm. * * Argument: An ALARM ID value that indicates which alarm should be canceled. */ @@ -184,6 +189,21 @@ #define RTC_RD_ALARM _RTCIOC(0x0007) +/* RTC_SET_PERIODIC set a periodic wakeup. + * + * Argument: A read-only reference to a struct rtc_setperiodic_s containing the + * new wakeup period to be set. + */ + +#define RTC_SET_PERIODIC _RTCIOC(0x0008) + +/* RTC_CANCEL_PERIODIC cancel the periodic wakeup. + * + * Argument: An ID value that indicates which wakeup should be canceled. + */ + +#define RTC_CANCEL_PERIODIC _RTCIOC(0x0009) + /* Architecture-specific RTC IOCTLS should begin at RTC_USER_IOCBASE. For * example: * @@ -192,7 +212,7 @@ * etc. */ -#define RTC_USER_IOCBASE 0x0008 +#define RTC_USER_IOCBASE 0x000a /**************************************************************************** @@ -268,7 +288,7 @@ struct lower_setalarm_s { uint8_t id; /* Indicates the alarm to be set */ rtc_alarm_callback_t cb; /* Callback when the alarm expires */ - FAR void *priv; /* Private argurment to accompany callback */ + FAR void *priv; /* Private argument to accompany callback */ struct rtc_time time; /* Alarm time */ }; @@ -278,7 +298,7 @@ struct lower_setrelative_s { uint8_t id; /* Indicates the alarm to be set */ rtc_alarm_callback_t cb; /* Callback when the alarm expires */ - FAR void *priv; /* Private argurment to accompany callback */ + FAR void *priv; /* Private argument to accompany callback */ time_t reltime; /* Relative time in seconds */ }; @@ -287,11 +307,42 @@ struct lower_setrelative_s struct lower_rdalarm_s { uint8_t id; /* Indicates the alarm to be set */ - FAR void *priv; /* Private argurment to accompany callback */ + FAR void *priv; /* Private argument to accompany callback */ FAR struct rtc_time *time;/* Queried RTC time pointer */ }; #endif +#ifdef CONFIG_RTC_PERIODIC + +/* Structure used with the RTC_SET_PERIODIC IOCTL command. */ + +struct rtc_setperiodic_s +{ + uint8_t id; /* Indicates the alarm to be set */ + uint8_t signo; /* Signal number for alarm notification */ + pid_t pid; /* Identifies task to be notified (0=caller) */ + union sigval sigvalue; /* Data passed with notification */ + struct timespec period; /* Period between wakeups */ +}; + +/* Callback type used by the RTC harware to notify the RTC driver when the + * wakeup period expires. + */ + +typedef CODE void (*rtc_wakeup_callback_t)(FAR void *priv, int alarmid); + +/* Structure used with the setperiodic method */ + +struct lower_setperiodic_s +{ + uint8_t id; /* Indicates the wakeup to be set */ + rtc_wakeup_callback_t cb; /* Callback when the wakeup expires */ + FAR void *priv; /* Private argument to accompany callback */ + struct timespec period; /* Period between wakeups */ +}; + +#endif + /* The RTC driver is implemented as a common, upper-half character driver * that provides the RTC driver structure and a lower-level, hardware * specific implementation that performs the actual RTC operations. @@ -342,6 +393,17 @@ struct rtc_ops_s FAR struct lower_rdalarm_s *alarminfo); #endif +#ifdef CONFIG_RTC_PERIODIC + /* setperiodic sets up a new periodic wakeup. */ + + CODE int (*setperiodic)(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo); + + /* cancelperiodic cancels the current periodic wakeup. */ + + CODE int (*cancelperiodic)(FAR struct rtc_lowerhalf_s *lower, int alarmid); +#endif + #ifdef CONFIG_RTC_IOCTL /* Support for architecture-specific RTC operations */