1 /***************************************************************************//**
2  * @file
3  * @brief HFXO Manager API implementation.
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2019 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_device.h"
32 #include "sl_hfxo_manager.h"
33 #include "sli_hfxo_manager.h"
34 #include "sli_hfxo_manager_internal.h"
35 #include "sl_sleeptimer.h"
36 #include "sl_assert.h"
37 #include "sl_status.h"
38 #include <stdbool.h>
39 
40 /*******************************************************************************
41  *********************************   DEFINES   *********************************
42  ******************************************************************************/
43 // Table size of HFXO wake-up time measurement
44 #define HFXO_STARTUP_TIME_TABLE_SIZE  10
45 
46 // Default time value in microseconds required to wake-up the hfxo oscillator.
47 #define HFXO_STARTUP_TIME_DEFAULT_VALUE_US  (600u)
48 
49 /*******************************************************************************
50  *****************************   DATA TYPES   **********************************
51  ******************************************************************************/
52 
53 /*******************************************************************************
54  ***************************  LOCAL VARIABLES   ********************************
55  ******************************************************************************/
56 
57 // Time in ticks required for HFXO start-up after wake-up from sleep.
58 static uint32_t hfxo_startup_time_tick = 0;
59 
60 static uint32_t hfxo_last_startup_time = 0;
61 
62 static uint32_t hfxo_startup_time_table[HFXO_STARTUP_TIME_TABLE_SIZE];
63 
64 static uint8_t hfxo_startup_time_table_index = 0;
65 
66 static uint32_t hfxo_startup_time_sum_average = 0;
67 
68 static uint32_t hfxo_startup_time_tc_initial = 0;
69 
70 static bool hfxo_measurement_on = false;
71 
72 /*******************************************************************************
73  **************************   GLOBAL FUNCTIONS   *******************************
74  ******************************************************************************/
75 
76 /***************************************************************************//**
77  * HFXO Manager module hardware specific initialization.
78  ******************************************************************************/
sl_hfxo_manager_init_hardware(void)79 void sl_hfxo_manager_init_hardware(void)
80 {
81   sli_hfxo_manager_init_hardware();
82 }
83 
84 /***************************************************************************//**
85  * Initialize HFXO Manager module.
86  ******************************************************************************/
sl_hfxo_manager_init(void)87 sl_status_t sl_hfxo_manager_init(void)
88 {
89   sl_status_t status;
90 
91   // Initialize Sleeptimer module in case not already done.
92   status = sl_sleeptimer_init();
93   if (status != SL_STATUS_OK) {
94     return status;
95   }
96 #if defined(HFXO_MANAGER_SLEEPTIMER_SYSRTC_INTEGRATION_ON)
97   // Additional Sleeptimer HW configuration if SYSRTC is used
98   sli_sleeptimer_hal_hfxo_manager_integration_init();
99 #endif
100 
101   // Set HFXO startup time to conservative default value
102   hfxo_startup_time_tick = (((HFXO_STARTUP_TIME_DEFAULT_VALUE_US * sl_sleeptimer_get_timer_frequency()) + (1000000 - 1)) / 1000000);
103   for (uint8_t i = 0; i < HFXO_STARTUP_TIME_TABLE_SIZE; i++) {
104     hfxo_startup_time_table[i] = hfxo_startup_time_tick;
105     hfxo_startup_time_sum_average += hfxo_startup_time_tick;
106   }
107 
108   return SL_STATUS_OK;
109 }
110 
111 /***************************************************************************//**
112  * Updates Sleepy Crystal settings.
113  *
114  * @param  settings  Pointer to settings structure
115  *
116  * @return Status Code.
117  *
118  * @note Those settings are temporarily used to force oscillation on sleepy
119  *       crystal.
120  *       Default values should be enough to wake-up sleepy crystals. Otherwise,
121  *       this function can be used.
122  ******************************************************************************/
sl_hfxo_manager_update_sleepy_xtal_settings(sl_hfxo_manager_sleepy_xtal_settings_t * settings)123 sl_status_t sl_hfxo_manager_update_sleepy_xtal_settings(sl_hfxo_manager_sleepy_xtal_settings_t *settings)
124 {
125   return sli_hfxo_manager_update_sleepy_xtal_settings_hardware(settings);
126 }
127 
128 /***************************************************************************//**
129  * When this callback function is called, it means that HFXO failed twice in
130  * a row to start with normal configurations. This may mean that there is a
131  * bad crystal. When getting this callback, HFXO is running but its properties
132  * (frequency, precision) are not guaranteed. This should be considered as an
133  * error situation.
134  ******************************************************************************/
sl_hfxo_manager_notify_consecutive_failed_startups(void)135 __WEAK void sl_hfxo_manager_notify_consecutive_failed_startups(void)
136 {
137   EFM_ASSERT(false);
138 }
139 
140 /*******************************************************************************
141  **********************   GLOBAL INTERNAL FUNCTIONS   **************************
142  ******************************************************************************/
143 
144 /***************************************************************************//**
145  * Function to call just before starting HFXO, to save current tick count.
146  ******************************************************************************/
sli_hfxo_manager_begin_startup_measurement(void)147 void sli_hfxo_manager_begin_startup_measurement(void)
148 {
149   hfxo_measurement_on = true;
150   hfxo_startup_time_tc_initial = sl_sleeptimer_get_tick_count();
151 }
152 
153 #if defined(HFXO_MANAGER_SLEEPTIMER_SYSRTC_INTEGRATION_ON)
154 /***************************************************************************//**
155  * Function to retrieve the capture channel value that was saved when
156  * HFXO became enabled.
157  *
158  * @note SYSRTC Capture channel is used to save when HFXO becomes enabled.
159  *       The HFXO startup measurement will only be done based on the capture
160  *       channel if the capture value is valid.
161  ******************************************************************************/
sli_hfxo_manager_retrieve_begining_startup_measurement(void)162 void sli_hfxo_manager_retrieve_begining_startup_measurement(void)
163 {
164   // ULFRCO does not have the precision to measure the HFXO startup time.
165   // So just return if ULFRCO is used as source oscillator.
166   if (sl_sleeptimer_get_timer_frequency() <= SystemULFRCOClockGet()) {
167     return;
168   }
169 
170   uint32_t startup_time = sli_sleeptimer_get_capture();
171 
172   if (startup_time != 0) {
173     hfxo_startup_time_tc_initial = startup_time;
174     hfxo_measurement_on = true;
175   }
176 }
177 #endif
178 
179 /***************************************************************************//**
180  * Function to call just after HFXO becomes ready, to save current tick count
181  * and calculate HFXO startup time.
182  ******************************************************************************/
sli_hfxo_manager_end_startup_measurement(void)183 void sli_hfxo_manager_end_startup_measurement(void)
184 {
185   uint32_t default_startup_ticks;
186 
187   if (hfxo_measurement_on == false) {
188     return;
189   }
190 
191   hfxo_last_startup_time = sl_sleeptimer_get_tick_count() - hfxo_startup_time_tc_initial;
192 
193   // With low precision clock, the HFXO startup time measure could be zero.
194   // In that case, ensure it's a least 1 tick.
195   hfxo_last_startup_time = (hfxo_last_startup_time == 0) ? 1 : hfxo_last_startup_time;
196 
197   // Skip measurement if value is out of bound
198   default_startup_ticks = (((HFXO_STARTUP_TIME_DEFAULT_VALUE_US * sl_sleeptimer_get_timer_frequency()) + (1000000 - 1)) / 1000000);
199   EFM_ASSERT(hfxo_last_startup_time <= default_startup_ticks);
200   if (hfxo_last_startup_time > default_startup_ticks) {
201     hfxo_measurement_on = false;
202     return;
203   }
204 
205   // Calculate average for HFXO restore time
206   hfxo_startup_time_sum_average -= (int32_t)hfxo_startup_time_table[hfxo_startup_time_table_index] - (int32_t)hfxo_last_startup_time;
207   hfxo_startup_time_table[hfxo_startup_time_table_index] = hfxo_last_startup_time;
208   hfxo_startup_time_tick = ((hfxo_startup_time_sum_average + (HFXO_STARTUP_TIME_TABLE_SIZE - 1) ) / HFXO_STARTUP_TIME_TABLE_SIZE);
209 
210   // Update index of wakeup time table
211   hfxo_startup_time_table_index++;
212   hfxo_startup_time_table_index %= HFXO_STARTUP_TIME_TABLE_SIZE;
213 
214   hfxo_measurement_on = false;
215 }
216 
217 /***************************************************************************//**
218  * Retrieves HFXO startup time average value.
219  *
220  * @return  HFXO startup time average value.
221  ******************************************************************************/
sli_hfxo_manager_get_startup_time(void)222 uint32_t sli_hfxo_manager_get_startup_time(void)
223 {
224   return hfxo_startup_time_tick;
225 }
226 
227 /***************************************************************************//**
228  * Retrieves HFXO startup time latest value.
229  *
230  * @return  HFXO startup time latest value.
231  ******************************************************************************/
sli_hfxo_manager_get_latest_startup_time(void)232 uint32_t sli_hfxo_manager_get_latest_startup_time(void)
233 {
234   return hfxo_last_startup_time;
235 }
236