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, pres);
58 }
59 
MXC_WUT_Shutdown(void)60 void MXC_WUT_Shutdown(void)
61 {
62     MXC_WUT_RevA_Shutdown((mxc_wut_reva_regs_t *)MXC_WUT);
63 }
64 
65 /* ************************************************************************** */
MXC_WUT_Enable(void)66 void MXC_WUT_Enable(void)
67 {
68     MXC_WUT_RevA_Enable((mxc_wut_reva_regs_t *)MXC_WUT);
69 }
70 
71 /* ************************************************************************** */
MXC_WUT_Disable(void)72 void MXC_WUT_Disable(void)
73 {
74     MXC_WUT_RevA_Disable((mxc_wut_reva_regs_t *)MXC_WUT);
75 }
76 
77 /* ************************************************************************** */
MXC_WUT_Config(const mxc_wut_cfg_t * cfg)78 void MXC_WUT_Config(const mxc_wut_cfg_t *cfg)
79 {
80     MXC_WUT_RevA_Config((mxc_wut_reva_regs_t *)MXC_WUT, (mxc_wut_reva_cfg_t *)cfg);
81 }
82 
83 /* ************************************************************************** */
MXC_WUT_GetCompare(void)84 uint32_t MXC_WUT_GetCompare(void)
85 {
86     return MXC_WUT_RevA_GetCompare((mxc_wut_reva_regs_t *)MXC_WUT);
87 }
88 
89 /* ************************************************************************* */
MXC_WUT_GetCount(void)90 uint32_t MXC_WUT_GetCount(void)
91 {
92     return MXC_WUT_RevA_GetCount((mxc_wut_reva_regs_t *)MXC_WUT);
93 }
94 
95 /* ************************************************************************* */
MXC_WUT_IntClear(void)96 void MXC_WUT_IntClear(void)
97 {
98     MXC_WUT_RevA_IntClear((mxc_wut_reva_regs_t *)MXC_WUT);
99 }
100 
101 /* ************************************************************************* */
MXC_WUT_IntStatus(void)102 uint32_t MXC_WUT_IntStatus(void)
103 {
104     return MXC_WUT_RevA_IntStatus((mxc_wut_reva_regs_t *)MXC_WUT);
105 }
106 
107 /* ************************************************************************* */
MXC_WUT_SetCompare(uint32_t cmp_cnt)108 void MXC_WUT_SetCompare(uint32_t cmp_cnt)
109 {
110     MXC_WUT_RevA_SetCompare((mxc_wut_reva_regs_t *)MXC_WUT, cmp_cnt);
111 }
112 
113 /* ************************************************************************* */
MXC_WUT_SetCount(uint32_t cnt)114 void MXC_WUT_SetCount(uint32_t cnt)
115 {
116     MXC_WUT_RevA_SetCount((mxc_wut_reva_regs_t *)MXC_WUT, cnt);
117 }
118 
119 /* ************************************************************************* */
MXC_WUT_GetTicks(uint32_t time,mxc_wut_unit_t units,uint32_t * ticks)120 int MXC_WUT_GetTicks(uint32_t time, mxc_wut_unit_t units, uint32_t *ticks)
121 {
122     return MXC_WUT_RevA_GetTicks((mxc_wut_reva_regs_t *)MXC_WUT, ERTCO_FREQ, time, units, ticks);
123 }
124 
125 /* ************************************************************************* */
MXC_WUT_GetTime(uint32_t ticks,uint32_t * time,mxc_wut_unit_t * units)126 int MXC_WUT_GetTime(uint32_t ticks, uint32_t *time, mxc_wut_unit_t *units)
127 {
128     return MXC_WUT_RevA_GetTime((mxc_wut_reva_regs_t *)MXC_WUT, ERTCO_FREQ, ticks, time,
129                                 (mxc_wut_reva_unit_t *)units);
130 }
131 
132 /* ************************************************************************** */
MXC_WUT_Edge(void)133 void MXC_WUT_Edge(void)
134 {
135     MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
136 }
137 
138 /* ************************************************************************** */
MXC_WUT_Store(void)139 void MXC_WUT_Store(void)
140 {
141     MXC_WUT_RevA_Store((mxc_wut_reva_regs_t *)MXC_WUT);
142 }
143 
144 /* ************************************************************************** */
MXC_WUT_RestoreBBClock(uint32_t dbbFreq)145 void MXC_WUT_RestoreBBClock(uint32_t dbbFreq)
146 {
147     MXC_WUT_RevA_RestoreBBClock((mxc_wut_reva_regs_t *)MXC_WUT, dbbFreq, ERTCO_FREQ);
148 }
149 
150 /* ************************************************************************** */
MXC_WUT_GetSleepTicks(void)151 uint32_t MXC_WUT_GetSleepTicks(void)
152 {
153     return MXC_WUT_RevA_GetSleepTicks((mxc_wut_reva_regs_t *)MXC_WUT);
154 }
155 
156 /* ************************************************************************** */
MXC_WUT_Delay_MS(uint32_t waitMs)157 void MXC_WUT_Delay_MS(uint32_t waitMs)
158 {
159     MXC_WUT_RevA_Delay_MS((mxc_wut_reva_regs_t *)MXC_WUT, waitMs, ERTCO_FREQ);
160 }
161 
162 /* ************************************************************************** */
MXC_WUT_GetWUTSync(uint32_t * wutCnt,uint32_t * snapshot)163 static void MXC_WUT_GetWUTSync(uint32_t *wutCnt, uint32_t *snapshot)
164 {
165     MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
166     *wutCnt = MXC_WUT->cnt;
167     *snapshot = MXC_WUT->snapshot;
168 }
169 
170 /* ************************************************************************** */
MXC_WUT_SetTrim(uint32_t trimValue)171 static void MXC_WUT_SetTrim(uint32_t trimValue)
172 {
173     MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_X1TRIM,
174                  (trimValue << MXC_F_TRIMSIR_RTC_X1TRIM_POS));
175     MXC_SETFIELD(MXC_TRIMSIR->rtc, MXC_F_TRIMSIR_RTC_X2TRIM,
176                  (trimValue << MXC_F_TRIMSIR_RTC_X2TRIM_POS));
177 }
178 
179 /* ************************************************************************** */
MXC_WUT_StarTrim(void)180 static int MXC_WUT_StarTrim(void)
181 {
182     uint32_t wutCnt0, wutCnt1;
183     uint32_t snapshot0, snapshot1;
184     uint32_t trimValue;
185 
186     /* Make sure the WUT is running in compare mode */
187     if (!(MXC_WUT->ctrl & MXC_F_WUT_REVA_CTRL_TEN)) {
188         return E_UNINITIALIZED;
189     }
190 
191     /* Make sure that DBB counter is running */
192     MXC_WUT_GetWUTSync(&wutCnt0, &snapshot0);
193     MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
194     if (snapshot0 == snapshot1) {
195         return E_UNINITIALIZED;
196     }
197 
198     /* Start with existing trim value */
199     trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_X1TRIM) >> MXC_F_TRIMSIR_RTC_X1TRIM_POS;
200     MXC_WUT_SetTrim(trimValue);
201 
202     /* Initialize the variables */
203     bestTrim_async = trimValue;
204     bestDiff_async = 0xFFFF;
205 
206     /* Get the initial snapshot */
207     MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
208 
209     trimPending = 1;
210 
211     return E_NO_ERROR;
212 }
213 
214 /* ************************************************************************** */
MXC_WUT_Handler(void)215 int MXC_WUT_Handler(void)
216 {
217     uint32_t wutCnt1;
218     uint32_t snapshot1;
219     uint32_t trimValue;
220     uint32_t snapTicks, wutTicks;
221     uint64_t calcTicks;
222     int trimComplete;
223     mxc_wut_complete_cb_t cbTemp;
224 
225     /* Clear the interrupt flags */
226     MXC_WUT_IntClear();
227 
228     if (!trimPending) {
229         return E_NO_ERROR;
230     }
231 
232     /* Store the snapshot */
233     MXC_WUT_GetWUTSync(&wutCnt1, &snapshot1);
234     snapTicks = snapshot1 - snapshot0_async;
235     wutTicks = wutCnt1 - wutCnt0_async;
236 
237     /* Calculate the ideal number of DBB ticks in WUT_TRIM_TICKS */
238     calcTicks = ((uint64_t)wutTicks * (uint64_t)BB_CLK_RATE_HZ) / (uint64_t)32768;
239 
240     trimComplete = 0;
241     trimValue = (MXC_TRIMSIR->rtc & MXC_F_TRIMSIR_RTC_X1TRIM) >> MXC_F_TRIMSIR_RTC_X1TRIM_POS;
242 
243     if (snapTicks > calcTicks) {
244         /* See if we're closer to the calculated value */
245         if ((snapTicks - calcTicks) <= bestDiff_async) {
246             bestDiff_async = snapTicks - calcTicks;
247             bestTrim_async = trimValue;
248         }
249 
250         /* Running slow, reduce cap */
251         if (trimValue == 0) {
252             /* We're maxed out on trim range */
253             trimComplete = 1;
254         }
255         trimValue--;
256 
257         if (capAdded_async == 1) {
258             /* We've hit an inflection point */
259             trimComplete = 1;
260         }
261         capAdded_async = -1;
262 
263     } else if (snapTicks < calcTicks) {
264         /* See if we're closer to the calculated value */
265         if ((calcTicks - snapTicks) <= bestDiff_async) {
266             bestDiff_async = calcTicks - snapTicks;
267             bestTrim_async = trimValue;
268         }
269 
270         /* Running fast, increase cap */
271         if (trimValue == 0x1f) {
272             /* We're maxed out on trim range */
273             trimComplete = 1;
274         }
275         trimValue++;
276 
277         if (capAdded_async == -1) {
278             /* We've hit an inflection point */
279             trimComplete = 1;
280         }
281         capAdded_async = 1;
282 
283     } else {
284         /* Just right */
285         bestTrim_async = trimValue;
286         trimComplete = 1;
287     }
288 
289     if (trimComplete) {
290         /* Apply the best trim value */
291         MXC_WUT_SetTrim(bestTrim_async);
292 
293         trimPending = 0;
294 
295         /* Call the callback */
296         if (cb_async != NULL) {
297             cbTemp = cb_async;
298             cb_async = NULL;
299             cbTemp(E_NO_ERROR);
300         }
301 
302         return E_NO_ERROR;
303     }
304 
305     /* Start the next step */
306     MXC_WUT_SetTrim(trimValue);
307     MXC_WUT_GetWUTSync(&wutCnt0_async, &snapshot0_async);
308 
309     if (cb_async != NULL) {
310         /* Prime the compare interrupt */
311         MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
312     }
313 
314     /* Return E_BUSY to indicate the trim procedure is still running */
315     return E_BUSY;
316 }
317 
318 /* ************************************************************************** */
MXC_WUT_TrimCrystal(void)319 int MXC_WUT_TrimCrystal(void)
320 {
321     int err, i;
322 
323     /* Clear the async callback pointer */
324     cb_async = NULL;
325 
326     /* Start the trim procedure */
327     err = MXC_WUT_StarTrim();
328     if (err != E_NO_ERROR) {
329         return err;
330     }
331     do {
332         for (i = 0; i < (WUT_TRIM_TICKS - 1); i++) {
333             MXC_WUT_RevA_Edge((mxc_wut_reva_regs_t *)MXC_WUT);
334         }
335     } while (MXC_WUT_Handler() != E_NO_ERROR);
336 
337     return E_NO_ERROR;
338 }
339 
340 /* ************************************************************************** */
MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)341 int MXC_WUT_TrimCrystalAsync(mxc_wut_complete_cb_t cb)
342 {
343     int err;
344 
345     if (cb == NULL) {
346         return E_NULL_PTR;
347     }
348 
349     /* Save the callback */
350     cb_async = cb;
351 
352     /* Start the trim procedure */
353     err = MXC_WUT_StarTrim();
354     if (err != E_NO_ERROR) {
355         return err;
356     }
357 
358     /* Prime the compare interrupt */
359     MXC_WUT->cmp = MXC_WUT->cnt + WUT_TRIM_TICKS - 1;
360 
361     return E_NO_ERROR;
362 }
363 
364 /* ************************************************************************** */
MXC_WUT_TrimPending(void)365 int MXC_WUT_TrimPending(void)
366 {
367     if (trimPending) {
368         return E_BUSY;
369     }
370 
371     return E_NO_ERROR;
372 }
373