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_ERTCO);
56 #endif
57 MXC_WUT_RevA_Init((mxc_wut_reva_regs_t *)MXC_WUT, (mxc_wut_reva_pres_t)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, ERTCO_FREQ, time,
130 (mxc_wut_reva_unit_t)units, ticks);
131 }
132
133 /* ************************************************************************* */
MXC_WUT_GetTime(uint32_t ticks,uint32_t * time,mxc_wut_unit_t * units)134 int MXC_WUT_GetTime(uint32_t ticks, uint32_t *time, mxc_wut_unit_t *units)
135 {
136 return MXC_WUT_RevA_GetTime((mxc_wut_reva_regs_t *)MXC_WUT, ERTCO_FREQ, ticks, time,
137 (mxc_wut_reva_unit_t *)units);
138 }
139
140 /* ************************************************************************** */
MXC_WUT_Edge(void)141 void MXC_WUT_Edge(void)
142 {
143 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
144 }
145
146 /* ************************************************************************** */
MXC_WUT_Store(void)147 void MXC_WUT_Store(void)
148 {
149 MXC_WUT_RevA_Store((mxc_wut_reva_regs_t *)MXC_WUT);
150 }
151
152 /* ************************************************************************** */
MXC_WUT_RestoreBBClock(uint32_t dbbFreq)153 void MXC_WUT_RestoreBBClock(uint32_t dbbFreq)
154 {
155 MXC_WUT_RevA_RestoreBBClock((mxc_wut_reva_regs_t *)MXC_WUT, dbbFreq, ERTCO_FREQ);
156 }
157
158 /* ************************************************************************** */
MXC_WUT_GetSleepTicks(void)159 uint32_t MXC_WUT_GetSleepTicks(void)
160 {
161 return MXC_WUT_RevA_GetSleepTicks((mxc_wut_reva_regs_t *)MXC_WUT);
162 }
163
164 /* ************************************************************************** */
MXC_WUT_Delay_MS(uint32_t waitMs)165 void MXC_WUT_Delay_MS(uint32_t waitMs)
166 {
167 MXC_WUT_RevA_Delay_MS((mxc_wut_reva_regs_t *)MXC_WUT, waitMs, ERTCO_FREQ);
168 }
169
170 /* ************************************************************************** */
MXC_WUT_GetWUTSync(uint32_t * wutCnt,uint32_t * snapshot)171 static void MXC_WUT_GetWUTSync(uint32_t *wutCnt, uint32_t *snapshot)
172 {
173 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
174 *wutCnt = MXC_WUT->cnt;
175 *snapshot = MXC_WUT->snapshot;
176 }
177
178 /* ************************************************************************** */
MXC_WUT_SetTrim(uint32_t trimValue)179 static void MXC_WUT_SetTrim(uint32_t trimValue)
180 {
181 MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_RTCX1,
182 (trimValue << MXC_F_TRIMSIR_RTC_RTCX1_POS));
183 MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_RTCX2,
184 (trimValue << MXC_F_TRIMSIR_RTC_RTCX2_POS));
185 }
186
187 /* ************************************************************************** */
MXC_WUT_StarTrim(void)188 static int MXC_WUT_StarTrim(void)
189 {
190 uint32_t wutCnt0, wutCnt1;
191 uint32_t snapshot0, snapshot1;
192 uint32_t trimValue;
193
194 /* Make sure the WUT is running in compare mode */
195 if (!(MXC_WUT->ctrl & MXC_F_WUT_REVA_CTRL_TEN)) {
196 return E_UNINITIALIZED;
197 }
198
199 if ((MXC_WUT->ctrl & MXC_F_WUT_CTRL_TMODE) != MXC_S_WUT_CTRL_TMODE_COMPARE) {
200 return E_UNINITIALIZED;
201 }
202
203 /* Make sure that DBB counter is running */
204 MXC_WUT_GetWUTSync(&wutCnt0, &snapshot0);
205 MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
206 if (snapshot0 == snapshot1) {
207 return E_UNINITIALIZED;
208 }
209
210 /* Start with existing trim value */
211 trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_RTCX1) >> MXC_F_TRIMSIR_RTC_RTCX1_POS;
212 MXC_WUT_SetTrim(trimValue);
213
214 /* Initialize the variables */
215 bestTrim_async = trimValue;
216 bestDiff_async = 0xFFFF;
217
218 /* Get the initial snapshot */
219 MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
220
221 trimPending = 1;
222
223 return E_NO_ERROR;
224 }
225
226 /* ************************************************************************** */
MXC_WUT_Handler(void)227 int MXC_WUT_Handler(void)
228 {
229 uint32_t wutCnt1;
230 uint32_t snapshot1;
231 uint32_t trimValue;
232 uint32_t snapTicks, wutTicks;
233 uint64_t calcTicks;
234 int trimComplete;
235 mxc_wut_complete_cb_t cbTemp;
236
237 /* Clear the interrupt flags */
238 MXC_WUT_IntClear();
239
240 if (!trimPending) {
241 return E_NO_ERROR;
242 }
243
244 /* Store the snapshot */
245 MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
246 snapTicks = snapshot1 - snapshot0_async;
247 wutTicks = wutCnt1 - wutCnt0_async;
248
249 /* Calculate the ideal number of DBB ticks in WUT_TRIM_TICKS */
250 calcTicks = ((uint64_t)wutTicks * (uint64_t)BB_CLK_RATE_HZ) / (uint64_t)32768;
251
252 trimComplete = 0;
253 trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_RTCX1) >> MXC_F_TRIMSIR_RTC_RTCX1_POS;
254
255 if (snapTicks > calcTicks) {
256 /* See if we're closer to the calculated value */
257 if ((snapTicks - calcTicks) <= bestDiff_async) {
258 bestDiff_async = snapTicks - calcTicks;
259 bestTrim_async = trimValue;
260 }
261
262 /* Running slow, reduce cap */
263 if (trimValue == 0) {
264 /* We're maxed out on trim range */
265 trimComplete = 1;
266 }
267 trimValue--;
268
269 if (capAdded_async == 1) {
270 /* We've hit an inflection point */
271 trimComplete = 1;
272 }
273 capAdded_async = -1;
274
275 } else if (snapTicks < calcTicks) {
276 /* See if we're closer to the calculated value */
277 if ((calcTicks - snapTicks) <= bestDiff_async) {
278 bestDiff_async = calcTicks - snapTicks;
279 bestTrim_async = trimValue;
280 }
281
282 /* Running fast, increase cap */
283 if (trimValue == 0x1f) {
284 /* We're maxed out on trim range */
285 trimComplete = 1;
286 }
287 trimValue++;
288
289 if (capAdded_async == -1) {
290 /* We've hit an inflection point */
291 trimComplete = 1;
292 }
293 capAdded_async = 1;
294
295 } else {
296 /* Just right */
297 bestTrim_async = trimValue;
298 trimComplete = 1;
299 }
300
301 if (trimComplete) {
302 /* Apply the best trim value */
303 MXC_WUT_SetTrim(bestTrim_async);
304
305 trimPending = 0;
306
307 /* Call the callback */
308 if (cb_async != NULL) {
309 cbTemp = cb_async;
310 cb_async = NULL;
311 cbTemp(E_NO_ERROR);
312 }
313
314 return E_NO_ERROR;
315 }
316
317 /* Start the next step */
318 MXC_WUT_SetTrim(trimValue);
319 MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
320
321 if (cb_async != NULL) {
322 /* Prime the compare interrupt */
323 MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
324 }
325
326 /* Return E_BUSY to indicate the trim procedure is still running */
327 return E_BUSY;
328 }
329
330 /* ************************************************************************** */
MXC_WUT_TrimCrystal(void)331 int MXC_WUT_TrimCrystal(void)
332 {
333 int err, i;
334
335 /* Clear the async callback pointer */
336 cb_async = NULL;
337
338 /* Start the trim procedure */
339 err = MXC_WUT_StarTrim();
340 if (err != E_NO_ERROR) {
341 return err;
342 }
343 do {
344 for (i = 0; i < (WUT_TRIM_TICKS - 1); i++) {
345 MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
346 }
347 } while (MXC_WUT_Handler() != E_NO_ERROR);
348
349 return E_NO_ERROR;
350 }
351
352 /* ************************************************************************** */
MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)353 int MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)
354 {
355 int err;
356
357 if (cb == NULL) {
358 return E_NULL_PTR;
359 }
360
361 /* Save the callback */
362 cb_async = cb;
363
364 /* Start the trim procedure */
365 err = MXC_WUT_StarTrim();
366 if (err != E_NO_ERROR) {
367 return err;
368 }
369
370 /* Prime the compare interrupt */
371 MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
372
373 return E_NO_ERROR;
374 }
375
376 /* ************************************************************************** */
MXC_WUT_TrimPending(void)377 int MXC_WUT_TrimPending(void)
378 {
379 if (trimPending) {
380 return E_BUSY;
381 }
382
383 return E_NO_ERROR;
384 }
385