1 /*
2 * Copyright (c) 2017-2019, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQueueNTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== PowerCC26X2_calibrateRCOSC.c ========
34 */
35
36 #include <stdbool.h>
37
38 #include <ti/drivers/dpl/HwiP.h>
39
40 #include <ti/drivers/Power.h>
41 #include <ti/drivers/power/PowerCC26X2.h>
42
43 #include <ti/devices/DeviceFamily.h>
44 #include DeviceFamily_constructPath(inc/hw_aux_evctl.h)
45 #include DeviceFamily_constructPath(inc/hw_aux_smph.h)
46 #include DeviceFamily_constructPath(inc/hw_aux_sysif.h)
47 #include DeviceFamily_constructPath(inc/hw_aux_tdc.h)
48 #include DeviceFamily_constructPath(inc/hw_ddi_0_osc.h)
49 #include DeviceFamily_constructPath(inc/hw_ddi.h)
50 #include DeviceFamily_constructPath(driverlib/aon_batmon.h)
51 #include DeviceFamily_constructPath(driverlib/ddi.h)
52 #include DeviceFamily_constructPath(driverlib/ioc.h)
53 #include DeviceFamily_constructPath(driverlib/osc.h)
54 #include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
55
56 #define AUX_TDC_SEMAPHORE_NUMBER 1 /* semaphore 1 protects TDC */
57 #define NUM_RCOSC_LF_PERIODS_TO_MEASURE 32 /* x RCOSC_LF periods vs XOSC_HF */
58 #define NUM_RCOSC_HF_PERIODS_TO_MEASURE 1 /* x RCOSC_HF periods vs XOSC_HF */
59 #define ACLK_REF_SRC_RCOSC_HF 0 /* Use RCOSC_HF for ACLK REF */
60 #define ACLK_REF_SRC_RCOSC_LF 2 /* Use RCOSC_LF for ACLK REF */
61 #define SCLK_LF_OPTION_RCOSC_LF 3 /* defined in cc26_ccfg.xls */
62 #define RCOSC_HF_LOW_THRESHOLD_TDC_VALUE 1535 /* If TDC value is within threshold range, no need for another TDC measurement */
63 #define RCOSC_HF_PERFECT_TDC_VALUE 1536 /* RCOSC_HF runs at perfect 48 MHz when ending up with this TDC value */
64 #define RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE 1537 /* If TDC value is within threshold range, no need for another TDC measurement */
65
66 #define DDI_0_OSC_O_CTL1_LOCAL 0x00000004 /* offset */
67 #define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M 0x007C0000 /* mask */
68 #define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S 18 /* shift */
69 #define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M 0x00020000 /* mask */
70 #define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S 17 /* shift */
71 #define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M 0x00000C00 /* offset */
72 #define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S 10 /* shift */
73
74 /* AUX ISR states */
75 #define WAIT_SMPH 0 /* just took SMPH, start RCOSC_LF */
76 #define CAL_RCOSC_LF 1 /* just finished RCOSC_LF, start first RCOSC_HF */
77 #define CAL_RCOSC_HF1 2 /* just finished 1st RCOSC_HF, start 2nd */
78 #define CAL_RCOSC_HF2 3 /* just finished 2nd RCOSC_HF, decide best */
79
80 /* calibration states */
81 #define PowerCC26X2_STATE_TDC_INIT 0
82 #define PowerCC26X2_STATE_CAL_LF_1 1
83 #define PowerCC26X2_STATE_CAL_LF_2 2
84 #define PowerCC26X2_STATE_CAL_HF1_1 3
85 #define PowerCC26X2_STATE_CAL_HF1_2 4
86 #define PowerCC26X2_STATE_CAL_HF2 5
87 #define PowerCC26X2_STATE_CLEANUP 6
88
89 /* FSM results */
90 typedef enum {
91 PowerCC26X2_FSM_RESULT_RUN_FSM,
92 PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC,
93 PowerCC26X2_FSM_RESULT_DONE,
94 PowerCC26X2_FSM_RESULT_ERROR,
95 } PowerCC26X2_FsmResult;
96
97 /* macros */
98 #define Min(a,b) (((a)<(b))?(a):(b))
99 #define Max(a,b) (((a)>(b))?(a):(b))
100 #define Abs(x) ((x) < 0 ? -(x) : (x))
101 #define Scale_rndInf(x) ((3 * (x) + (((x) < 0) ? -2 : 2)) / 4)
102
103 #ifndef PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
104 #define PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION 0
105 #endif
106
107 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
108 volatile unsigned int gotSEM = 0;
109 volatile unsigned int calLFi = 0;
110 volatile unsigned int calHF1i = 0;
111 volatile unsigned int calHF2i = 0;
112 volatile bool doneCal = false;
113 unsigned int tdcResult_LF = 0;
114 unsigned int tdcResult_HF1 = 0;
115 unsigned int tdcResult_HF2 = 0;
116 unsigned int numISRs = 0;
117 #endif
118
119 /* Forward declarations */
120 static bool getTdcSemaphore();
121 static void updateSubSecInc(uint32_t tdcResult);
122 static void calibrateRcoscHf1(int32_t tdcResult);
123 static void calibrateRcoscHf2(int32_t tdcResult);
124 static PowerCC26X2_FsmResult runCalibrateFsm(void);
125 void PowerCC26X2_calibrate(void);
126 void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg);
127
128 /* Externs */
129 extern PowerCC26X2_ModuleState PowerCC26X2_module;
130 extern const PowerCC26X2_Config PowerCC26X2_config;
131
132 /*
133 * ======== PowerCC26X2_initiateCalibration ========
134 * Initiate calibration of RCOSC_LF and RCOSCHF
135 */
PowerCC26X2_initiateCalibration()136 bool PowerCC26X2_initiateCalibration()
137 {
138 unsigned int hwiKey;
139 bool busy = false;
140 bool status;
141 bool gotSem;
142
143 if ((PowerCC26X2_module.calLF == false) &&
144 (PowerCC26X2_config.calibrateRCOSC_HF == false)) {
145 return (false);
146 }
147
148 /* make sure calibration is not already in progress */
149 hwiKey = HwiP_disable();
150
151 if (PowerCC26X2_module.busyCal == false) {
152 PowerCC26X2_module.busyCal = true;
153 }
154 else {
155 busy = true;
156 }
157
158 HwiP_restore(hwiKey);
159
160 if (busy == true) {
161 return (false);
162 }
163
164 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
165 gotSEM = 0;
166 calLFi = 0;
167 calHF1i = 0;
168 calHF2i = 0;
169 doneCal = false;
170 #endif
171
172 /* set contraint to prohibit standby during calibration sequence */
173 Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
174
175 /* set dependency to keep XOSC_HF active during calibration sequence */
176 Power_setDependency(PowerCC26XX_XOSC_HF);
177
178 /* initiate acquisition of semaphore protecting TDC */
179 gotSem = getTdcSemaphore();
180
181 /* if didn't acquire semaphore, must wait for autotake ISR */
182 if (gotSem == false) {
183 PowerCC26X2_module.auxHwiState = WAIT_SMPH;
184 status = false; /* false: don't do anything else until acquire SMPH */
185 }
186
187 /* else, semaphore acquired, OK to proceed with first measurement */
188 else {
189 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
190 gotSEM = 1;
191 #endif
192 status = true; /* true: OK to start first measurement */
193 }
194
195 return (status);
196 }
197
198 /*
199 * ======== PowerCC26X2_auxISR ========
200 * ISR for the AUX combo interrupt event. Implements Hwi state machine to
201 * step through the RCOSC calibration steps.
202 */
PowerCC26X2_auxISR(uintptr_t arg)203 void PowerCC26X2_auxISR(uintptr_t arg)
204 {
205 uint32_t tdcResult;
206
207 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
208 numISRs++;
209 #endif
210
211 /*
212 * disable all events that are part of AUX_COMBINED_INTERRUPT.
213 * This interrupt is reserved for use during RCOSC calibration.
214 * Other AUX perihperals that want to generate interrupts to CM3
215 * must use dedicated interrupt lines or go through AON combined.
216 */
217 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0;
218
219 /* ****** state = WAIT_SMPH: arrive here if just took the SMPH ****** */
220 if (PowerCC26X2_module.auxHwiState == WAIT_SMPH) {
221 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
222 gotSEM = 1;
223 #endif
224 }
225
226 /* **** state = CAL_RCOSC_LF: here when just finished LF counting **** */
227 else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_LF) {
228
229 tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT);
230
231 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
232 tdcResult_LF = tdcResult;
233 #endif
234 /* update the RTC SUBSECINC register based on LF measurement result */
235 updateSubSecInc(tdcResult);
236 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
237 calLFi = 1;
238 #endif
239 /* if doing HF calibration initiate it now */
240 if (PowerCC26X2_config.calibrateRCOSC_HF) {
241 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_LF_2; /* next: trigger LF */
242 }
243
244 /* else, start cleanup */
245 else {
246 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */
247 }
248 }
249
250 /* ****** state = CAL_RCOSC_HF1: here when just finished 1st RCOSC_HF */
251 else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_HF1) {
252
253 tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT);
254
255 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
256 tdcResult_HF1 = tdcResult;
257 calHF1i = 1;
258 #endif
259
260 /* use first HF measurement to setup new trim values */
261 calibrateRcoscHf1(tdcResult);
262
263 /* if HF setting perfect, nothing more to do, calibration is done */
264 if ((tdcResult >= RCOSC_HF_LOW_THRESHOLD_TDC_VALUE) &&
265 (tdcResult <= RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE)) {
266 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */
267 }
268
269 /* else, tweak trims, initiate another HF measurement */
270 else {
271
272 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_2; /* next: HF meas. #2 */
273 }
274 }
275
276 /* ****** state = just finished second RCOSC_HF measurement ****** */
277 else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_HF2) {
278
279 tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT);
280
281 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
282 tdcResult_HF2 = tdcResult;
283 #endif
284 /* look for improvement on #2, else revert to previous trim values */
285 calibrateRcoscHf2(tdcResult);
286
287 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */
288 }
289
290 /* do the next calibration step... */
291 PowerCC26X2_calibrate();
292 }
293
294 /*
295 * ======== PowerCC26X2_calibrate ========
296 */
PowerCC26X2_calibrate(void)297 void PowerCC26X2_calibrate(void)
298 {
299 PowerCC26X2_FsmResult fsmResult;
300
301 do {
302 fsmResult = runCalibrateFsm();
303 } while (fsmResult == PowerCC26X2_FSM_RESULT_RUN_FSM);
304
305 switch (fsmResult) {
306 case PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC:
307 /* Intentional fall-through */
308 case PowerCC26X2_FSM_RESULT_DONE:
309 /* Do nothing. Calibration is complete or the
310 * TDC harware will execute in the background
311 * and continue the operation. */
312 break;
313 default:
314 /* Something went wrong. No good way to recover. */
315 while(1);
316 }
317 }
318
319 /*
320 * ======== runCalibrateFsm ========
321 * Execute one state of the clock calibration FSM.
322 */
runCalibrateFsm(void)323 static PowerCC26X2_FsmResult runCalibrateFsm(void) {
324
325 switch (PowerCC26X2_module.calStep) {
326
327 case PowerCC26X2_STATE_TDC_INIT:
328
329 /* Turn on TDC clock */
330 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) = AUX_SYSIF_TDCCLKCTL_REQ;
331
332 /* set saturation config to 2^24 */
333 HWREG(AUX_TDC_BASE + AUX_TDC_O_SATCFG) = AUX_TDC_SATCFG_LIMIT_R24;
334
335 /* set start and stop trigger sources and polarity */
336 HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGSRC) =
337 (AUX_TDC_TRIGSRC_STOP_SRC_ACLK_REF |
338 AUX_TDC_TRIGSRC_STOP_POL_HIGH) |
339 (AUX_TDC_TRIGSRC_START_SRC_ACLK_REF |
340 AUX_TDC_TRIGSRC_START_POL_HIGH);
341
342 /* set TDC_SRC clock to be XOSC_HF/2 = 24 MHz */
343 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE,
344 DDI_0_OSC_O_CTL0,
345 DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M,
346 DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S, 2);
347
348 /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */
349 DDI16BitfieldRead(AUX_DDI0_OSC_BASE,
350 DDI_0_OSC_O_CTL0,
351 DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M,
352 DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S);
353
354 /* set AUX_SYSIF:TDCCLKCTL.REQ... */
355 HWREG(AUX_SYSIF_BASE +AUX_SYSIF_O_TDCCLKCTL) = AUX_SYSIF_TDCCLKCTL_REQ;
356
357 /* finish wait for AUX_SYSIF:TDCCLKCTL.ACK to be set ... */
358 while(!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) &
359 AUX_SYSIF_TDCCLKCTL_ACK));
360
361 /* Enable trig count */
362 HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTCFG) = AUX_TDC_TRIGCNTCFG_EN;
363
364 /* if LF calibration enabled start LF measurement */
365 if (PowerCC26X2_module.calLF) {
366
367 /* clear UPD_REQ, new sub-second increment is NOT available */
368 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL) = 0;
369
370 /* set next Swi state */
371 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_LF_1;
372 }
373
374 /* else, start first HF measurement */
375 else {
376 /* set next Swi state */
377 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_1;
378 }
379
380 /* abort TDC */
381 HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_ABORT;
382
383 /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */
384 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0;
385
386 /* finish wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */
387 while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK);
388
389 return PowerCC26X2_FSM_RESULT_RUN_FSM;
390
391 case PowerCC26X2_STATE_CAL_LF_1:
392
393 /* set next Hwi state before triggering TDC */
394 PowerCC26X2_module.auxHwiState = CAL_RCOSC_LF;
395
396 /* set the ACLK reference clock */
397 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
398 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M,
399 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S,
400 ACLK_REF_SRC_RCOSC_LF);
401
402 /* set AUX_SYSIFTDCREFCLKCTL.REQ */
403 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ;
404
405 /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */
406 ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock));
407
408 return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC;
409
410 case PowerCC26X2_STATE_CAL_LF_2:
411
412 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_1;
413
414 /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */
415 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0;
416
417 /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */
418 while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK);
419
420 return PowerCC26X2_FSM_RESULT_RUN_FSM;
421
422 case PowerCC26X2_STATE_CAL_HF1_1:
423
424 PowerCC26X2_module.auxHwiState = CAL_RCOSC_HF1;
425
426 /* set the ACLK reference clock */
427 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE,
428 DDI_0_OSC_O_CTL0,
429 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M,
430 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S,
431 ACLK_REF_SRC_RCOSC_HF);
432
433 /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */
434 DDI16BitfieldRead(AUX_DDI0_OSC_BASE,
435 DDI_0_OSC_O_CTL0,
436 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M,
437 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M);
438
439 /* set AUX_SYSIFTDCREFCLKCTL.REQ */
440 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ;
441
442 /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */
443 ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock));
444
445 return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC;
446
447 case PowerCC26X2_STATE_CAL_HF1_2:
448
449 PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF2;
450
451 /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */
452 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0;
453
454 /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */
455 while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK);
456
457 return PowerCC26X2_FSM_RESULT_RUN_FSM;
458
459 case PowerCC26X2_STATE_CAL_HF2:
460
461 PowerCC26X2_module.auxHwiState = CAL_RCOSC_HF2;
462
463 /* set the ACLK reference clock */
464 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE,
465 DDI_0_OSC_O_CTL0,
466 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M,
467 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S,
468 ACLK_REF_SRC_RCOSC_HF);
469
470 /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */
471 DDI16BitfieldRead(AUX_DDI0_OSC_BASE,
472 DDI_0_OSC_O_CTL0,
473 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M,
474 DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M);
475
476 /* set AUX_SYSIFTDCREFCLKCTL.REQ */
477 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ;
478
479 /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */
480 ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock));
481
482 return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC;
483
484 case PowerCC26X2_STATE_CLEANUP:
485
486 /* release the TDC clock request */
487 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) = 0;
488
489 /* release the TDC reference clock request */
490 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0;
491
492 /* wait for AUX_SYSIF:TDCCLKCTL.ACK to be cleared ... */
493 while ((HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) &
494 AUX_SYSIF_TDCCLKCTL_ACK));
495 /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */
496 while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) &
497 AUX_SYSIF_TDCREFCLKCTL_ACK);
498
499 /*
500 * Disable all interrupts as part of AUX_COMBINED interrupt
501 * Once we release semaphore, the sensor controller is allowed
502 * to use the TDC. When it does, we must ensure that this
503 * does not cause any unexpected interrupts to the CM3.
504 */
505 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0;
506
507 /* release AUX semaphore */
508 HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1) = 1;
509
510 /* release the power down constraints and XOSC_HF dependency */
511 Power_releaseDependency(PowerCC26XX_XOSC_HF);
512 Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
513
514 /* set next state */
515 PowerCC26X2_module.calStep = PowerCC26X2_STATE_TDC_INIT;
516
517 #if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION
518 doneCal = true;
519 calHF2i = 1;
520 #endif
521 PowerCC26X2_module.busyCal = false;
522
523 return PowerCC26X2_FSM_RESULT_DONE;
524
525 default:
526 return PowerCC26X2_FSM_RESULT_ERROR;
527 }
528 }
529
PowerCC26X2_RCOSC_clockFunc(uintptr_t arg)530 void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg) {
531
532 /* Wait any remaining time for TDCREFCLKCTL_ACK. Should not spin here at all. */
533 while(!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK));
534
535 /* Set number of periods of ACLK to count */
536 if (PowerCC26X2_module.calStep == PowerCC26X2_STATE_CAL_LF_1) {
537 HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = NUM_RCOSC_LF_PERIODS_TO_MEASURE;
538 }
539 else {
540 HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = NUM_RCOSC_HF_PERIODS_TO_MEASURE;
541 }
542
543 /* Reset/clear result of TDC */
544 HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_CLR_RESULT;
545
546 /* Clear possible pending interrupt source */
547 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_TDC_DONE;
548
549 /* Enable TDC done interrupt as part of AUX_COMBINED interrupt */
550 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_TDC_DONE;
551
552 /* Run TDC (start synchronously) */
553 HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_RUN_SYNC_START;
554 }
555
556 /*
557 * ======== getTdcSemaphore ========
558 * Get TDC semaphore (number 1)
559 */
getTdcSemaphore()560 static bool getTdcSemaphore()
561 {
562 unsigned int own;
563
564 /* try to acquire SMPH */
565 own = HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1);
566
567 /* if acquired SMPH: done */
568 if (own != 0) {
569 return (true);
570 }
571
572 /* clear the interrupt source, can only be cleared when we don't have semaphore */
573 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_SMPH_AUTOTAKE_DONE;
574
575 /*
576 * else, did not acquire the semaphore, enable SMPH_AUTOTAKE_DONE event
577 * (don't OR, write entire register, no other interrupts can be enabled!)
578 */
579 HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = AUX_EVCTL_COMBEVTOMCUMASK_AUX_SMPH_AUTOTAKE_DONE;
580
581 /* start AUTOTAKE of semaphore for TDC access */
582 HWREG(AUX_SMPH_BASE + AUX_SMPH_O_AUTOTAKE) = AUX_TDC_SEMAPHORE_NUMBER;
583
584 return (false);
585 }
586
587 /*
588 * ======== updateSubSecInc ========
589 * Update the SUBSECINC register based on measured RCOSC_LF frequency
590 */
updateSubSecInc(uint32_t tdcResult)591 static void updateSubSecInc(uint32_t tdcResult)
592 {
593 int32_t newSubSecInc;
594 uint32_t oldSubSecInc;
595 uint32_t subSecInc;
596 int32_t hposcOffset;
597 int32_t hposcOffsetInv;
598
599 /*
600 * Calculate the new SUBSECINC
601 * Here's the formula: AON_RTC:SUBSECINC = (45813 * NR) / 256
602 * Based on measuring 32 LF clock periods
603 */
604 newSubSecInc = (45813 * tdcResult) / 256;
605
606 /* Compensate HPOSC drift if HPOSC is in use */
607 if(OSC_IsHPOSCEnabled()) {
608 /* Get the HPOSC relative offset at this temperature */
609 hposcOffset = OSC_HPOSCRelativeFrequencyOffsetGet(AONBatMonTemperatureGetDegC());
610 /* Convert to RF core format */
611 hposcOffsetInv = OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(hposcOffset);
612 /* Adjust SUBSECINC */
613 newSubSecInc += (((newSubSecInc >> 4) * (hposcOffsetInv >> 3)) >> 15);
614 }
615
616 /* Apply filter, but not for first calibration */
617 if (PowerCC26X2_module.firstLF) {
618 /* Don't apply filter first time, to converge faster */
619 subSecInc = newSubSecInc;
620 /* No longer first measurement */
621 PowerCC26X2_module.firstLF = false;
622 }
623 else {
624 /* Read old SUBSECINC value */
625 oldSubSecInc = HWREG(AON_RTC_BASE + AON_RTC_O_SUBSECINC) & 0x00FFFFFF;
626 /* Apply filter, 0.5 times old value, 0.5 times new value */
627 subSecInc = (oldSubSecInc * 1 + newSubSecInc * 1) / 2;
628 }
629
630 /* Update SUBSECINC values */
631 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC0) = subSecInc;
632 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC1) = subSecInc >> 16;
633
634 /* update to use new values */
635 HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL) = AUX_SYSIF_RTCSUBSECINCCTL_UPD_REQ;
636 }
637
638 /*
639 * ======== PowerCC26X2_calibrateRcoscHf1 ========
640 * Calibrate RCOSC_HF agains XOSC_HF: compute and setup new trims
641 */
calibrateRcoscHf1(int32_t tdcResult)642 static void calibrateRcoscHf1(int32_t tdcResult)
643 {
644 /* *** STEP 1: Find RCOSC_HF-XOSC_HF frequency offset with current trim settings */
645 /* Read in current trim settings */
646 PowerCC26X2_module.nCtrimCurr =
647 (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL) &
648 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M) >>
649 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S;
650
651 PowerCC26X2_module.nCtrimFractCurr =
652 (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL)
653 & DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M) >>
654 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S;
655
656 PowerCC26X2_module.nRtrimCurr =
657 (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL)
658 & DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M) >>
659 DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S;
660
661
662 /*
663 * Find RCOSC_HF-XOSC_HF frequency offset with current trim settings
664 * Positive value => RCOSC_HF runs slow, CTRIM(FRACT) should be increased
665 * Negative value => RCOSC_HF runs fast, CTRIM(FRACT) should be decreased
666 * Resolution: 31.25 kHz; CTRIMFRACT resolution ~30 kHz
667 */
668 PowerCC26X2_module.nDeltaFreqCurr = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE;
669
670 /* *** STEP 2: Attempt to calculate more optimal settings */
671 if (PowerCC26X2_module.nDeltaFreqCurr == 0) {
672 /* If perfect, don't perform second measurement and keep current settings */
673 PowerCC26X2_module.bRefine = false;
674 return;
675 }
676 if (PowerCC26X2_module.bRefine) {
677 /*
678 * Trying to find better match across CTRIM/RTRIM. Due to mismatches the
679 * first try might not have been more optimal than the current setting.
680 * Continue refining, starting from stored values
681 */
682 } else {
683 /* Start from current values */
684 PowerCC26X2_module.nCtrimFractNew = PowerCC26X2_module.nCtrimFractCurr;
685 PowerCC26X2_module.nCtrimNew = PowerCC26X2_module.nCtrimCurr;
686 PowerCC26X2_module.nRtrimNew = PowerCC26X2_module.nRtrimCurr;
687 PowerCC26X2_module.nDeltaFreqNew = PowerCC26X2_module.nDeltaFreqCurr;
688 }
689
690 /*
691 * Calculate change to CTRIMFRACT with safe assumptions of gain,
692 * apply delta to current CTRIMFRACT and convert to valid CTRIM/CTRIMFRACT
693 */
694 PowerCC26X2_module.nCtrimFractNew = PowerCC26X2_module.nCtrimFractNew +
695 Scale_rndInf(PowerCC26X2_module.nDeltaFreqNew);
696 PowerCC26X2_module.nCtrimNew = PowerCC26X2_module.nCtrimCurr;
697
698 /* One step of CTRIM is about 500 kHz, so limit to one CTRIM step */
699 if (PowerCC26X2_module.nCtrimFractNew < 1) {
700 if (PowerCC26X2_module.nRtrimNew == 3) {
701 /* We try the slow RTRIM in this CTRIM first */
702 PowerCC26X2_module.nCtrimFractNew = Max(1, PowerCC26X2_module.nCtrimFractNew + 21);
703 PowerCC26X2_module.nRtrimNew = 0;
704 }
705 else {
706 /* Step down one CTRIM and use fast RTRIM */
707 PowerCC26X2_module.nCtrimFractNew = Max(1, PowerCC26X2_module.nCtrimFractNew + 32 - 21);
708 PowerCC26X2_module.nCtrimNew = Max(0, PowerCC26X2_module.nCtrimNew - 1);
709 PowerCC26X2_module.nRtrimNew = 3;
710 }
711 }
712 else if (PowerCC26X2_module.nCtrimFractNew > 30) {
713 if (PowerCC26X2_module.nRtrimNew == 0) {
714 /* We try the slow RTRIM in this CTRIM first */
715 PowerCC26X2_module.nCtrimFractNew = Min(30, PowerCC26X2_module.nCtrimFractNew - 21);
716 PowerCC26X2_module.nRtrimNew = 3;
717 }
718 else {
719 /* Step down one CTRIM and use fast RTRIM */
720 PowerCC26X2_module.nCtrimFractNew = Min(30, PowerCC26X2_module.nCtrimFractNew - 32 + 21);
721 PowerCC26X2_module.nCtrimNew = Min(0x3F, PowerCC26X2_module.nCtrimNew + 1);
722 PowerCC26X2_module.nRtrimNew = 0;
723 }
724 }
725 else
726 {
727 /* We're within sweet spot of current CTRIM => no change */
728 }
729
730 /* Find RCOSC_HF vs XOSC_HF frequency offset with new trim settings */
731 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL,
732 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M,
733 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S,
734 PowerCC26X2_module.nCtrimNew);
735
736 /* Enable RCOSCHFCTRIMFRACT_EN */
737 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL,
738 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M,
739 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S,
740 1);
741
742 /* Modify CTRIM_FRACT */
743 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL,
744 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M,
745 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S,
746 PowerCC26X2_module.nCtrimFractNew);
747
748 /* Modify RTRIM */
749 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL,
750 DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M,
751 DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S,
752 PowerCC26X2_module.nRtrimNew);
753 }
754
755 /*
756 * ======== Power_calibrateRcoscHf2 ========
757 * Calibrate RCOSC_HF agains XOSC_HF: determine better result, set new trims
758 */
calibrateRcoscHf2(int32_t tdcResult)759 static void calibrateRcoscHf2(int32_t tdcResult)
760 {
761
762 PowerCC26X2_module.nDeltaFreqNew = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE;
763 /* Calculate new delta freq */
764
765 /* *** STEP 4: Determine whether the new settings are better or worse */
766 if (Abs(PowerCC26X2_module.nDeltaFreqNew) <= Abs(PowerCC26X2_module.nDeltaFreqCurr)) {
767 /* New settings are better or same -> make current by keeping in registers */
768 PowerCC26X2_module.bRefine = false;
769 }
770 else {
771 /* First measurement was better than second, restore current settings */
772 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL,
773 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M,
774 DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S,
775 PowerCC26X2_module.nCtrimCurr);
776
777 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL,
778 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M,
779 DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S,
780 PowerCC26X2_module.nCtrimFractCurr);
781
782 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL,
783 DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M,
784 DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S,
785 PowerCC26X2_module.nRtrimCurr);
786
787 /* Enter a refinement mode where we keep searching for better matches */
788 PowerCC26X2_module.bRefine = true;
789 }
790
791 }
792