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