1 /******************************************************************************
2 *
3 * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4 * Analog Devices, Inc.),
5 * Copyright (C) 2023-2024 Analog Devices, Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ******************************************************************************/
20
21 /* **** Includes **** */
22 #include <stddef.h>
23 #include "mxc_device.h"
24 #include "mxc_assert.h"
25 #include "wut.h"
26 #include "wut_reva.h"
27 #include "trimsir_regs.h"
28
29 /* **** Definitions **** */
30
31 /* Clock rate the BLE DBB counter */
32 #ifndef BB_CLK_RATE_HZ
33 #define BB_CLK_RATE_HZ 1000000
34 #endif
35
36 /* Higher values will produce a more accurate measurement, but will consume more power */
37 #define WUT_TRIM_TICKS 0x1000
38
39 /* **** Globals **** */
40
41 /* **** Local Variables **** */
42
43 /* Used for the asynchronous trim procedure */
44 static uint32_t wutCnt0_async, snapshot0_async, bestTrim_async, bestDiff_async;
45 static int capAdded_async;
46 static int trimPending;
47 static mxc_wut_complete_cb_t cb_async;
48
49 /* **** Functions **** */
50
51 /* ************************************************************************** */
MXC_WUT_Init(mxc_wut_pres_t pres)52 void MXC_WUT_Init(mxc_wut_pres_t pres)
53 {
54 #ifndef MSDK_NO_GPIO_CLK_INIT
55 MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_XTAL32K);
56 #endif
57 MXC_WUT_RevA_Init((mxc_wut_reva_regs_t *)MXC_WUT, pres);
58 }
59
60 /* ************************************************************************** */
MXC_WUT_Shutdown(void)61 void MXC_WUT_Shutdown(void)
62 {
63 MXC_WUT_RevA_Shutdown((mxc_wut_reva_regs_t *)MXC_WUT);
64 }
65
66 /* ************************************************************************** */
MXC_WUT_Enable(void)67 void MXC_WUT_Enable(void)
68 {
69 MXC_WUT_RevA_Enable((mxc_wut_reva_regs_t *)MXC_WUT);
70 }
71
72 /* ************************************************************************** */
MXC_WUT_Disable(void)73 void MXC_WUT_Disable(void)
74 {
75 MXC_WUT_RevA_Disable((mxc_wut_reva_regs_t *)MXC_WUT);
76 }
77
78 /* ************************************************************************** */
MXC_WUT_Config(const mxc_wut_cfg_t * cfg)79 void MXC_WUT_Config(const mxc_wut_cfg_t *cfg)
80 {
81 MXC_WUT_RevA_Config((mxc_wut_reva_regs_t *)MXC_WUT, (mxc_wut_reva_cfg_t *)cfg);
82 }
83
84 /* ************************************************************************** */
MXC_WUT_GetCompare(void)85 uint32_t MXC_WUT_GetCompare(void)
86 {
87 return MXC_WUT_RevA_GetCompare((mxc_wut_reva_regs_t *)MXC_WUT);
88 }
89
90 /* ************************************************************************** */
MXC_WUT_GetCapture(void)91 uint32_t MXC_WUT_GetCapture(void)
92 {
93 return MXC_WUT_RevA_GetCapture((mxc_wut_reva_regs_t *)MXC_WUT);
94 }
95
96 /* ************************************************************************* */
MXC_WUT_GetCount(void)97 uint32_t MXC_WUT_GetCount(void)
98 {
99 return MXC_WUT_RevA_GetCount((mxc_wut_reva_regs_t *)MXC_WUT);
100 }
101
102 /* ************************************************************************* */
MXC_WUT_IntClear(void)103 void MXC_WUT_IntClear(void)
104 {
105 MXC_WUT_RevA_IntClear((mxc_wut_reva_regs_t *)MXC_WUT);
106 }
107
108 /* ************************************************************************* */
MXC_WUT_IntStatus(void)109 uint32_t MXC_WUT_IntStatus(void)
110 {
111 return MXC_WUT_RevA_IntStatus((mxc_wut_reva_regs_t *)MXC_WUT);
112 }
113
114 /* ************************************************************************* */
MXC_WUT_SetCompare(uint32_t cmp_cnt)115 void MXC_WUT_SetCompare(uint32_t cmp_cnt)
116 {
117 MXC_WUT_RevA_SetCompare((mxc_wut_reva_regs_t *)MXC_WUT, cmp_cnt);
118 }
119
120 /* ************************************************************************* */
MXC_WUT_SetCount(uint32_t cnt)121 void MXC_WUT_SetCount(uint32_t cnt)
122 {
123 MXC_WUT_RevA_SetCount((mxc_wut_reva_regs_t *)MXC_WUT, cnt);
124 }
125
126 /* ************************************************************************* */
MXC_WUT_GetTicks(uint32_t time,mxc_wut_unit_t units,uint32_t * ticks)127 int MXC_WUT_GetTicks(uint32_t time, mxc_wut_unit_t units, uint32_t *ticks)
128 {
129 return MXC_WUT_RevA_GetTicks((mxc_wut_reva_regs_t *)MXC_WUT, XTAL32K_FREQ, time, units, ticks);
130 }
131
132 /* ************************************************************************* */
MXC_WUT_GetTime(uint32_t ticks,uint32_t * time,mxc_wut_unit_t * units)133 int MXC_WUT_GetTime(uint32_t ticks, uint32_t *time, mxc_wut_unit_t *units)
134 {
135 return MXC_WUT_RevA_GetTime((mxc_wut_reva_regs_t *)MXC_WUT, XTAL32K_FREQ, ticks, time,
136 (mxc_wut_reva_unit_t *)units);
137 }
138
139 /* ************************************************************************** */
MXC_WUT_Edge(void)140 void MXC_WUT_Edge(void)
141 {
142 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
143 }
144
145 /* ************************************************************************** */
MXC_WUT_Store(void)146 void MXC_WUT_Store(void)
147 {
148 MXC_WUT_RevA_Store((mxc_wut_reva_regs_t *)MXC_WUT);
149 }
150
151 /* ************************************************************************** */
MXC_WUT_RestoreBBClock(uint32_t dbbFreq)152 void MXC_WUT_RestoreBBClock(uint32_t dbbFreq)
153 {
154 MXC_WUT_RevA_RestoreBBClock((mxc_wut_reva_regs_t *)MXC_WUT, dbbFreq, XTAL32K_FREQ);
155 }
156
157 /* ************************************************************************** */
MXC_WUT_GetSleepTicks(void)158 uint32_t MXC_WUT_GetSleepTicks(void)
159 {
160 return MXC_WUT_RevA_GetSleepTicks((mxc_wut_reva_regs_t *)MXC_WUT);
161 }
162
163 /* ************************************************************************** */
MXC_WUT_Delay_MS(uint32_t waitMs)164 void MXC_WUT_Delay_MS(uint32_t waitMs)
165 {
166 MXC_WUT_RevA_Delay_MS((mxc_wut_reva_regs_t *)MXC_WUT, waitMs, XTAL32K_FREQ);
167 }
168
169 /* ************************************************************************** */
MXC_WUT_GetWUTSync(uint32_t * wutCnt,uint32_t * snapshot)170 static void MXC_WUT_GetWUTSync(uint32_t *wutCnt, uint32_t *snapshot)
171 {
172 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
173 *wutCnt = MXC_WUT->cnt;
174 *snapshot = MXC_WUT->snapshot;
175 }
176
177 /* ************************************************************************** */
MXC_WUT_SetTrim(uint32_t trimValue)178 static void MXC_WUT_SetTrim(uint32_t trimValue)
179 {
180 MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_RTCX1,
181 (trimValue << MXC_F_TRIMSIR_RTC_RTCX1_POS));
182 MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_RTCX2,
183 (trimValue << MXC_F_TRIMSIR_RTC_RTCX2_POS));
184 }
185
186 /* ************************************************************************** */
MXC_WUT_StarTrim(void)187 static int MXC_WUT_StarTrim(void)
188 {
189 uint32_t wutCnt0, wutCnt1;
190 uint32_t snapshot0, snapshot1;
191 uint32_t trimValue;
192
193 /* Make sure the WUT is running in compare mode */
194 if (!(MXC_WUT->ctrl & MXC_F_WUT_REVA_CTRL_TEN)) {
195 return E_UNINITIALIZED;
196 }
197
198 if ((MXC_WUT->ctrl & MXC_F_WUT_CTRL_TMODE) != MXC_S_WUT_CTRL_TMODE_COMPARE) {
199 return E_UNINITIALIZED;
200 }
201
202 /* Make sure that DBB counter is running */
203 MXC_WUT_GetWUTSync(&wutCnt0, &snapshot0);
204 MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
205 if (snapshot0 == snapshot1) {
206 return E_UNINITIALIZED;
207 }
208
209 /* Start with existing trim value */
210 trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_RTCX1) >> MXC_F_TRIMSIR_RTC_RTCX1_POS;
211 MXC_WUT_SetTrim(trimValue);
212
213 /* Initialize the variables */
214 bestTrim_async = trimValue;
215 bestDiff_async = 0xFFFF;
216
217 /* Get the initial snapshot */
218 MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
219
220 trimPending = 1;
221
222 return E_NO_ERROR;
223 }
224
225 /* ************************************************************************** */
MXC_WUT_Handler(void)226 int MXC_WUT_Handler(void)
227 {
228 uint32_t wutCnt1;
229 uint32_t snapshot1;
230 uint32_t trimValue;
231 uint32_t snapTicks, wutTicks;
232 uint64_t calcTicks;
233 int trimComplete;
234 mxc_wut_complete_cb_t cbTemp;
235
236 /* Clear the interrupt flags */
237 MXC_WUT_IntClear();
238
239 if (!trimPending) {
240 return E_NO_ERROR;
241 }
242
243 /* Store the snapshot */
244 MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
245 snapTicks = snapshot1 - snapshot0_async;
246 wutTicks = wutCnt1 - wutCnt0_async;
247
248 /* Calculate the ideal number of DBB ticks in WUT_TRIM_TICKS */
249 calcTicks = ((uint64_t)wutTicks * (uint64_t)BB_CLK_RATE_HZ) / (uint64_t)32768;
250
251 trimComplete = 0;
252 trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_RTCX1) >> MXC_F_TRIMSIR_RTC_RTCX1_POS;
253
254 if (snapTicks > calcTicks) {
255 /* See if we're closer to the calculated value */
256 if ((snapTicks - calcTicks) <= bestDiff_async) {
257 bestDiff_async = snapTicks - calcTicks;
258 bestTrim_async = trimValue;
259 }
260
261 /* Running slow, reduce cap */
262 if (trimValue == 0) {
263 /* We're maxed out on trim range */
264 trimComplete = 1;
265 }
266 trimValue--;
267
268 if (capAdded_async == 1) {
269 /* We've hit an inflection point */
270 trimComplete = 1;
271 }
272 capAdded_async = -1;
273
274 } else if (snapTicks < calcTicks) {
275 /* See if we're closer to the calculated value */
276 if ((calcTicks - snapTicks) <= bestDiff_async) {
277 bestDiff_async = calcTicks - snapTicks;
278 bestTrim_async = trimValue;
279 }
280
281 /* Running fast, increase cap */
282 if (trimValue == 0x1f) {
283 /* We're maxed out on trim range */
284 trimComplete = 1;
285 }
286 trimValue++;
287
288 if (capAdded_async == -1) {
289 /* We've hit an inflection point */
290 trimComplete = 1;
291 }
292 capAdded_async = 1;
293
294 } else {
295 /* Just right */
296 bestTrim_async = trimValue;
297 trimComplete = 1;
298 }
299
300 if (trimComplete) {
301 /* Apply the best trim value */
302 MXC_WUT_SetTrim(bestTrim_async);
303
304 trimPending = 0;
305
306 /* Call the callback */
307 if (cb_async != NULL) {
308 cbTemp = cb_async;
309 cb_async = NULL;
310 cbTemp(E_NO_ERROR);
311 }
312
313 return E_NO_ERROR;
314 }
315
316 /* Start the next step */
317 MXC_WUT_SetTrim(trimValue);
318 MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
319
320 if (cb_async != NULL) {
321 /* Prime the compare interrupt */
322 MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
323 }
324
325 /* Return E_BUSY to indicate the trim procedure is still running */
326 return E_BUSY;
327 }
328
329 /* ************************************************************************** */
MXC_WUT_TrimCrystal(void)330 int MXC_WUT_TrimCrystal(void)
331 {
332 int err, i;
333
334 /* Clear the async callback pointer */
335 cb_async = NULL;
336
337 /* Start the trim procedure */
338 err = MXC_WUT_StarTrim();
339 if (err != E_NO_ERROR) {
340 return err;
341 }
342 do {
343 for (i = 0; i < (WUT_TRIM_TICKS - 1); i++) {
344 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
345 }
346 } while (MXC_WUT_Handler() != E_NO_ERROR);
347
348 return E_NO_ERROR;
349 }
350
351 /* ************************************************************************** */
MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)352 int MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)
353 {
354 int err;
355
356 if (cb == NULL) {
357 return E_NULL_PTR;
358 }
359
360 /* Save the callback */
361 cb_async = cb;
362
363 /* Start the trim procedure */
364 err = MXC_WUT_StarTrim();
365 if (err != E_NO_ERROR) {
366 return err;
367 }
368
369 /* Prime the compare interrupt */
370 MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
371
372 return E_NO_ERROR;
373 }
374
375 /* ************************************************************************** */
MXC_WUT_TrimPending(void)376 int MXC_WUT_TrimPending(void)
377 {
378 if (trimPending) {
379 return E_BUSY;
380 }
381
382 return E_NO_ERROR;
383 }
384