Skip to content

Commit d7c82cb

Browse files
stevew817bulislaw
authored andcommitted
Re-implement RTC for Silicon Labs targets
mbed RTC specifications now dictate that the RTC needs to retain and keep on counting through reset. On Silicon Labs parts, this means the RTC API can not be backed by the Silicon Labs RTC peripheral, since that doesn't provide retention functionality. Therefore: * On EFM32GG, EFM32WG, EFM32LG: mbed RTC API is now backed by BURTC. * On EFM32PG, EFR32MG, EFM32PG12, EFR32MG12: mbed RTC API is now backed by RTCC. * On EFM32ZG, EFM32HG: mbed RTC API is sadly no longer supported, since these chips don't have retained memory. # Conflicts: # targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c # targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api.c # targets/targets.json
1 parent 6cc8cf9 commit d7c82cb

File tree

7 files changed

+448
-428
lines changed

7 files changed

+448
-428
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/***************************************************************************//**
2+
* @file rtc_burtc.c
3+
*******************************************************************************
4+
* @section License
5+
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
6+
*******************************************************************************
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
11+
* not use this file except in compliance with the License.
12+
* You may obtain a copy of the License at
13+
*
14+
* http://www.apache.org/licenses/LICENSE-2.0
15+
*
16+
* Unless required by applicable law or agreed to in writing, software
17+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
* See the License for the specific language governing permissions and
20+
* limitations under the License.
21+
*
22+
******************************************************************************/
23+
24+
#include "device.h"
25+
#if DEVICE_RTC
26+
27+
/* Use BURTC on devices that have it, and don't use the RTCC abstraction */
28+
#if defined(BURTC_PRESENT) && !defined(RTCC_PRESENT)
29+
30+
#include "clocking.h"
31+
#include "em_cmu.h"
32+
#include "em_emu.h"
33+
#include "em_burtc.h"
34+
#include "rtc_api.h"
35+
36+
void rtc_init(void)
37+
{
38+
/* Only reset & configure the RTC if it has never run before */
39+
if(BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT)) {
40+
EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT;
41+
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
42+
em4_init.osc = emuEM4Osc_LFXO;
43+
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
44+
em4_init.osc = emuEM4Osc_LFRCO;
45+
#else
46+
#error "Can't use BURTC on mbed with ULFRCO"
47+
#endif
48+
EMU_EM4Init(&em4_init);
49+
50+
BURTC_Init_TypeDef burtc_init = BURTC_INIT_DEFAULT;
51+
burtc_init.mode = burtcModeEM4;
52+
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
53+
burtc_init.clkSel = burtcClkSelLFXO;
54+
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
55+
burtc_init.clkSel = burtcClkSelLFRCO;
56+
#else
57+
#error "Can't use BURTC on mbed with ULFRCO"
58+
#endif
59+
burtc_init.clkDiv = burtcClkDiv_128;
60+
burtc_init.lowPowerMode = burtcLPEnable;
61+
62+
BURTC_Reset();
63+
BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 0);
64+
BURTC_Init(&burtc_init);
65+
BURTC_RetRegSet(0, 0);
66+
BURTC_RetRegSet(1, 0);
67+
}
68+
}
69+
70+
void rtc_free(void)
71+
{
72+
/* Nothing to release here */
73+
}
74+
75+
int rtc_isenabled(void)
76+
{
77+
return (BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT) == 0);
78+
}
79+
80+
time_t rtc_read(void)
81+
{
82+
uint32_t ts = 0;
83+
uint32_t ts2 = 1;
84+
do {
85+
if (BURTC->IF & BURTC_IF_OF) {
86+
BURTC_RetRegSet(1, BURTC_RetRegGet(1)+1);
87+
BURTC->IFC = BURTC_IFC_OF;
88+
}
89+
90+
ts = ts2;
91+
ts2 = (BURTC_CounterGet() >> 8) + BURTC_RetRegGet(0) + (BURTC_RetRegGet(1) << 16);
92+
} while (ts != ts2);
93+
return ts2;
94+
}
95+
96+
void rtc_write(time_t t)
97+
{
98+
BURTC_RetRegSet(0, t - (BURTC_CounterGet() >> 8));
99+
BURTC_RetRegSet(1, 0);
100+
if (BURTC->IF & BURTC_IF_OF) {
101+
BURTC->IFC = BURTC_IFC_OF;
102+
}
103+
}
104+
105+
#endif /* BURTC_PRESENT && !RTCC_PRESENT */
106+
#endif /* DEVICE_RTC */

targets/TARGET_Silicon_Labs/TARGET_EFM32/common/mbed_overrides.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ void mbed_sdk_init()
117117
# error "Low energy clock selection not valid"
118118
#endif
119119

120+
#if defined(RTCC_PRESENT)
121+
/* Turn RTCC clock gate back on to keep RTC time correct */
122+
CMU_ClockEnable(cmuClock_RTCC, true);
123+
#endif
124+
120125
#if defined(EFM_BC_EN)
121126
/* Enable BC line driver to avoid garbage on CDC port */
122127
gpio_init_out_ex(&bc_enable, EFM_BC_EN, 1);

targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c

Lines changed: 123 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
* for the low power timer, which should be good enough for a low power use
3333
* case.
3434
*
35+
* Mapping of mbed APIs to Silicon Labs peripherals:
36+
* ---: Does not meet mbed API requirements
37+
* X : Implemented to provide mbed API functionality
38+
*
39+
* --------------------------------------------
40+
* | ------------- | RTCC | BURTC | RTC | TIMER |
41+
* | rtc_api | X | X | --- | ----- |
42+
* | lp_ticker_api | X | | X | ----- |
43+
* | us_ticker_api | --- | ----- | --- | X |
44+
* --------------------------------------------
45+
*
3546
* On Silicon Labs devices, the lowest width RTC implementation has a 24-bit
3647
* counter, which gets extended with a further 32-bit software counter. This
3748
* gives 56 bits of actual width, which with the default speed maps to
@@ -40,32 +51,122 @@
4051
* (At max speed the wraparound is at 69730 years, which is unlikely as well)
4152
******************************************************************************/
4253

43-
#include "rtc_api.h"
44-
#include "rtc_api_HAL.h"
54+
#if defined(RTC_PRESENT)
55+
#include "em_rtc.h"
56+
#include "em_cmu.h"
4557
#include "lp_ticker_api.h"
4658
#include "mbed_critical.h"
4759

48-
static int rtc_reserved = 0;
60+
#if RTC_CLOCKDIV_INT > 16
61+
#error invalid prescaler value RTC_CLOCKDIV_INT, since LP ticker resolution will exceed 1ms.
62+
#endif
63+
64+
#define RTC_BITS (24U)
65+
#define RTC_MAX_VALUE (0xFFFFFFUL)
66+
67+
static bool rtc_inited = false;
68+
static time_t time_base = 0;
69+
static uint32_t time_extend = 0;
70+
static uint32_t extended_comp0 = 0;
71+
72+
void RTC_IRQHandler(void)
73+
{
74+
uint32_t flags;
75+
flags = RTC_IntGet();
76+
if (flags & RTC_IF_OF) {
77+
RTC_IntClear(RTC_IF_OF);
78+
/* RTC has overflowed (24 bits). Use time_extend as software counter for 32 more bits. */
79+
time_extend += 1;
80+
}
81+
if (flags & RTC_IF_COMP0) {
82+
RTC_IntClear(RTC_IF_COMP0);
83+
if (lp_ticker_irq_handler != NULL && time_extend == extended_comp0) {
84+
lp_ticker_irq_handler();
85+
}
86+
}
87+
}
88+
89+
uint64_t rtc_get_full(void)
90+
{
91+
uint64_t ticks = 0;
92+
93+
do
94+
{
95+
/* In case someone's trying to read time in a critical section */
96+
if (RTC_IntGet() & RTC_IF_OF) {
97+
RTC_IntClear(RTC_IF_OF);
98+
time_extend += 1;
99+
}
100+
101+
ticks = (uint64_t)time_extend << RTC_BITS;
102+
ticks += RTC_CounterGet();
103+
}
104+
while ( (ticks & RTC_MAX_VALUE) != RTC_CounterGet() );
105+
106+
return ticks;
107+
}
49108

50109
void lp_ticker_init()
51110
{
52-
if(!rtc_reserved) {
53-
core_util_critical_section_enter();
54-
rtc_init_real(RTC_INIT_LPTIMER);
55-
rtc_set_comp0_handler((uint32_t)lp_ticker_irq_handler);
56-
rtc_reserved = 1;
57-
core_util_critical_section_exit();
111+
core_util_critical_section_enter();
112+
if (!rtc_inited) {
113+
CMU_ClockEnable(cmuClock_RTC, true);
114+
115+
/* Scale clock to save power */
116+
CMU_ClockDivSet(cmuClock_RTC, RTC_CLOCKDIV);
117+
118+
/* Initialize RTC */
119+
RTC_Init_TypeDef init = RTC_INIT_DEFAULT;
120+
init.enable = 1;
121+
/* Don't use compare register 0 as top value */
122+
init.comp0Top = 0;
123+
124+
/* Enable Interrupt from RTC */
125+
RTC_IntEnable(RTC_IEN_OF);
126+
NVIC_SetVector(RTC_IRQn, (uint32_t)RTC_IRQHandler);
127+
NVIC_EnableIRQ(RTC_IRQn);
128+
129+
/* Initialize */
130+
RTC_Init(&init);
131+
132+
rtc_inited = true;
58133
}
134+
core_util_critical_section_exit();
59135
}
60136

61137
void lp_ticker_free()
62138
{
63-
if(rtc_reserved) {
64-
core_util_critical_section_enter();
65-
rtc_free_real(RTC_INIT_LPTIMER);
66-
rtc_reserved = 0;
67-
core_util_critical_section_exit();
139+
/* Disable the RTC if it was inited and is no longer in use by anyone. */
140+
if (rtc_inited) {
141+
NVIC_DisableIRQ(RTC_IRQn);
142+
RTC_Reset();
143+
CMU_ClockEnable(cmuClock_RTC, false);
144+
rtc_inited = false;
145+
}
146+
}
147+
148+
void rtc_enable_comp0(bool enable)
149+
{
150+
RTC_FreezeEnable(true);
151+
if (!enable) {
152+
RTC_IntDisable(RTC_IF_COMP0);
153+
} else {
154+
RTC_IntEnable(RTC_IF_COMP0);
68155
}
156+
RTC_FreezeEnable(false);
157+
}
158+
159+
void rtc_set_comp0_value(uint64_t value, bool enable)
160+
{
161+
rtc_enable_comp0(false);
162+
163+
/* Set callback */
164+
RTC_FreezeEnable(true);
165+
extended_comp0 = (uint32_t) (value >> RTC_BITS);
166+
RTC_CompareSet(0, (uint32_t) (value & RTC_MAX_VALUE));
167+
RTC_FreezeEnable(false);
168+
169+
rtc_enable_comp0(enable);
69170
}
70171

71172
void lp_ticker_set_interrupt(timestamp_t timestamp)
@@ -96,17 +197,17 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
96197
rtc_set_comp0_value(rtc_compare_value, true);
97198
}
98199

99-
inline void lp_ticker_fire_interrupt(void)
200+
void lp_ticker_fire_interrupt(void)
100201
{
101-
rtc_force_comp0();
202+
RTC_IntSet(RTC_IFS_COMP0);
102203
}
103204

104-
inline void lp_ticker_disable_interrupt()
205+
void lp_ticker_disable_interrupt()
105206
{
106207
rtc_enable_comp0(false);
107208
}
108209

109-
inline void lp_ticker_clear_interrupt()
210+
void lp_ticker_clear_interrupt()
110211
{
111212
/* No need to clear interrupt flag, since that already happens at RTC level */
112213
}
@@ -127,4 +228,7 @@ timestamp_t lp_ticker_read()
127228
return (timestamp_t) (ticks_temp & 0xFFFFFFFF);
128229
}
129230

130-
#endif
231+
#elif defined(RTCC_PRESENT)
232+
/* lp_ticker api is implemented in rtc_rtcc.c */
233+
#endif /* RTC_PRESENT */
234+
#endif /* DEVICE_LOWPOWERTIMER */

0 commit comments

Comments
 (0)