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