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