1 /******************************************************************************
2 * Filename: osc.c
3 *
4 * Description: Driver for setting up the system Oscillators
5 *
6 * Copyright (c) 2015 - 2022, Texas Instruments Incorporated
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1) Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * 2) Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * 3) Neither the name of the ORGANIZATION nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 ******************************************************************************/
36
37 #include <stdlib.h>
38 #include "../inc/hw_types.h"
39 #include "../inc/hw_ccfg.h"
40 #include "../inc/hw_fcfg1.h"
41 #include "aon_batmon.h"
42 #include "aon_rtc.h"
43 #include "osc.h"
44 #include "sys_ctrl.h"
45 #include "setup_rom.h"
46
47 //*****************************************************************************
48 //
49 // Handle support for DriverLib in ROM:
50 // This section will undo prototype renaming made in the header file
51 //
52 //*****************************************************************************
53 #if !defined(DOXYGEN)
54 #undef OSCClockSourceSet
55 #define OSCClockSourceSet NOROM_OSCClockSourceSet
56 #undef OSCClockSourceGet
57 #define OSCClockSourceGet NOROM_OSCClockSourceGet
58 #undef OSCHF_GetStartupTime
59 #define OSCHF_GetStartupTime NOROM_OSCHF_GetStartupTime
60 #undef OSCHF_TurnOnXosc
61 #define OSCHF_TurnOnXosc NOROM_OSCHF_TurnOnXosc
62 #undef OSCHF_AttemptToSwitchToXosc
63 #define OSCHF_AttemptToSwitchToXosc NOROM_OSCHF_AttemptToSwitchToXosc
64 #undef OSCHF_SwitchToRcOscTurnOffXosc
65 #define OSCHF_SwitchToRcOscTurnOffXosc NOROM_OSCHF_SwitchToRcOscTurnOffXosc
66 #undef OSCHF_DebugGetCrystalAmplitude
67 #define OSCHF_DebugGetCrystalAmplitude NOROM_OSCHF_DebugGetCrystalAmplitude
68 #undef OSCHF_DebugGetExpectedAverageCrystalAmplitude
69 #define OSCHF_DebugGetExpectedAverageCrystalAmplitude NOROM_OSCHF_DebugGetExpectedAverageCrystalAmplitude
70 #undef OSCHF_DebugGetCrystalStartupTime
71 #define OSCHF_DebugGetCrystalStartupTime NOROM_OSCHF_DebugGetCrystalStartupTime
72 #undef OSC_HPOSCInitializeFrequencyOffsetParameters
73 #define OSC_HPOSCInitializeFrequencyOffsetParameters NOROM_OSC_HPOSCInitializeFrequencyOffsetParameters
74 #undef OSC_HPOSC_Debug_InitFreqOffsetParams
75 #define OSC_HPOSC_Debug_InitFreqOffsetParams NOROM_OSC_HPOSC_Debug_InitFreqOffsetParams
76 #undef OSC_HPOSCInitializeSingleInsertionFreqOffsParams
77 #define OSC_HPOSCInitializeSingleInsertionFreqOffsParams NOROM_OSC_HPOSCInitializeSingleInsertionFreqOffsParams
78 #undef OSC_HPOSCRelativeFrequencyOffsetGet
79 #define OSC_HPOSCRelativeFrequencyOffsetGet NOROM_OSC_HPOSCRelativeFrequencyOffsetGet
80 #undef OSC_AdjustXoscHfCapArray
81 #define OSC_AdjustXoscHfCapArray NOROM_OSC_AdjustXoscHfCapArray
82 #undef OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
83 #define OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert NOROM_OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
84 #undef OSC_HPOSCRtcCompensate
85 #define OSC_HPOSCRtcCompensate NOROM_OSC_HPOSCRtcCompensate
86 #endif
87
88 //*****************************************************************************
89 //
90 // OSCHF switch time calculator defines and globals
91 //
92 //*****************************************************************************
93
94 #define RTC_CV_TO_MS(x) (( 1000 * ( x )) >> 16 )
95 #define RTC_CV_TO_US(x) (( 1000000 * ( x )) >> 16 )
96
97 typedef struct {
98 uint32_t previousStartupTimeInUs ;
99 uint32_t timeXoscOff_CV ;
100 uint32_t timeXoscOn_CV ;
101 uint32_t timeXoscStable_CV ;
102 int32_t tempXoscOff ;
103 } OscHfGlobals_t;
104
105 static OscHfGlobals_t oscHfGlobals;
106
107 //*****************************************************************************
108 //
109 // Configure the oscillator input to the a source clock.
110 //
111 //*****************************************************************************
112 void
OSCClockSourceSet(uint32_t ui32SrcClk,uint32_t ui32Osc)113 OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc)
114 {
115 // Check the arguments.
116 ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
117 (ui32SrcClk & OSC_SRC_CLK_HF));
118 ASSERT((ui32Osc == OSC_RCOSC_HF) ||
119 (ui32Osc == OSC_RCOSC_LF) ||
120 (ui32Osc == OSC_XOSC_HF) ||
121 (ui32Osc == OSC_XOSC_LF));
122
123 // Request the high frequency source clock (using 24 MHz XTAL)
124 if(ui32SrcClk & OSC_SRC_CLK_HF)
125 {
126 // Enable the HF XTAL as HF clock source
127 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
128 DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_M,
129 DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_S,
130 ui32Osc);
131 }
132
133 // Configure the low frequency source clock.
134 if(ui32SrcClk & OSC_SRC_CLK_LF)
135 {
136 // Change the clock source.
137 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
138 DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_M,
139 DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_S,
140 ui32Osc);
141 }
142 }
143
144 //*****************************************************************************
145 //
146 // Get the source clock settings
147 //
148 //*****************************************************************************
149 uint32_t
OSCClockSourceGet(uint32_t ui32SrcClk)150 OSCClockSourceGet(uint32_t ui32SrcClk)
151 {
152 uint32_t ui32ClockSource;
153
154 // Check the arguments.
155 ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
156 (ui32SrcClk & OSC_SRC_CLK_HF));
157
158 // Return the source for the selected clock.
159 if(ui32SrcClk == OSC_SRC_CLK_LF)
160 {
161 ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
162 DDI_0_OSC_STAT0_SCLK_LF_SRC_M,
163 DDI_0_OSC_STAT0_SCLK_LF_SRC_S);
164 }
165 else
166 {
167 ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
168 DDI_0_OSC_STAT0_SCLK_HF_SRC_M,
169 DDI_0_OSC_STAT0_SCLK_HF_SRC_S);
170 }
171 return ( ui32ClockSource );
172 }
173
174 //*****************************************************************************
175 //
176 // Returns maximum startup time (in microseconds) of XOSC_HF
177 //
178 //*****************************************************************************
179 uint32_t
OSCHF_GetStartupTime(uint32_t timeUntilWakeupInMs)180 OSCHF_GetStartupTime( uint32_t timeUntilWakeupInMs )
181 {
182 uint32_t deltaTimeSinceXoscOnInMs ;
183 int32_t deltaTempSinceXoscOn ;
184 uint32_t newStartupTimeInUs ;
185
186 // Check CCFG to determine if device is configured for TCXO.
187 if( ( HWREG( CCFG_BASE + CCFG_O_MODE_CONF ) & CCFG_MODE_CONF_XOSC_FREQ_M ) == CCFG_MODE_CONF_XOSC_FREQ_TCXO )
188 {
189 // Device configured for TCXO. Report fixed startup time located in CCFG with
190 // coversion from number of 100us to number of us.
191 newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) & CCFG_MODE_CONF_1_TCXO_MAX_START_M ) >>
192 CCFG_MODE_CONF_1_TCXO_MAX_START_S ) * 100;
193 }
194 else
195 {
196 deltaTimeSinceXoscOnInMs = RTC_CV_TO_MS( AONRTCCurrentCompareValueGet() - oscHfGlobals.timeXoscOn_CV );
197 deltaTempSinceXoscOn = AONBatMonTemperatureGetDegC() - oscHfGlobals.tempXoscOff;
198
199 if ( deltaTempSinceXoscOn < 0 ) {
200 deltaTempSinceXoscOn = -deltaTempSinceXoscOn;
201 }
202
203 if ( (( timeUntilWakeupInMs + deltaTimeSinceXoscOnInMs ) > 3000 ) ||
204 ( deltaTempSinceXoscOn > 5 ) ||
205 ( oscHfGlobals.timeXoscStable_CV < oscHfGlobals.timeXoscOn_CV ) ||
206 ( oscHfGlobals.previousStartupTimeInUs == 0 ) )
207 {
208 newStartupTimeInUs = 2000;
209 if (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_XOSC_OVR_M ) == 0 ) {
210 newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
211 CCFG_MODE_CONF_1_XOSC_MAX_START_M ) >>
212 CCFG_MODE_CONF_1_XOSC_MAX_START_S ) * 125;
213 // Note: CCFG startup time is "in units of 100us" adding 25% margin results in *125
214 }
215 } else {
216 newStartupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
217 newStartupTimeInUs += ( newStartupTimeInUs >> 2 ); // Add 25 percent margin
218 if ( newStartupTimeInUs < oscHfGlobals.previousStartupTimeInUs ) {
219 newStartupTimeInUs = oscHfGlobals.previousStartupTimeInUs;
220 }
221 }
222
223 if ( newStartupTimeInUs < 200 ) {
224 newStartupTimeInUs = 200;
225 }
226 if ( newStartupTimeInUs > 4000 ) {
227 newStartupTimeInUs = 4000;
228 }
229 }
230 return ( newStartupTimeInUs );
231 }
232
233
234 //*****************************************************************************
235 //
236 // Turns on XOSC_HF (but without switching to XOSC_HF)
237 //
238 //*****************************************************************************
239 void
OSCHF_TurnOnXosc(void)240 OSCHF_TurnOnXosc( void )
241 {
242 #if ( defined( ROM_OSCClockSourceSet ))
243 ROM_OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_XOSC_HF );
244 #else
245 OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_XOSC_HF );
246 #endif
247 oscHfGlobals.timeXoscOn_CV = AONRTCCurrentCompareValueGet();
248 }
249
250
251 //*****************************************************************************
252 //
253 // Switch to XOSC_HF if XOSC_HF is ready.
254 //
255 //*****************************************************************************
256 bool
OSCHF_AttemptToSwitchToXosc(void)257 OSCHF_AttemptToSwitchToXosc( void )
258 {
259 uint32_t startupTimeInUs;
260 uint32_t prevLimmit25InUs;
261
262 #if ( defined( ROM_OSCClockSourceGet ))
263 if ( ROM_OSCClockSourceGet( OSC_SRC_CLK_HF ) == OSC_XOSC_HF )
264 #else
265 if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) == OSC_XOSC_HF )
266 #endif
267 {
268 // Already on XOSC - nothing to do
269 return ( 1 );
270 }
271 if ( OSCHfSourceReady()) {
272 OSCHfSourceSwitch();
273
274 // Store startup time, but limit to 25 percent reduction each time.
275 oscHfGlobals.timeXoscStable_CV = AONRTCCurrentCompareValueGet();
276 startupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
277 prevLimmit25InUs = oscHfGlobals.previousStartupTimeInUs;
278 prevLimmit25InUs -= ( prevLimmit25InUs >> 2 ); // 25 percent margin
279 oscHfGlobals.previousStartupTimeInUs = startupTimeInUs;
280 if ( prevLimmit25InUs > startupTimeInUs ) {
281 oscHfGlobals.previousStartupTimeInUs = prevLimmit25InUs;
282 }
283 return ( 1 );
284 }
285 return ( 0 );
286 }
287
288
289 //*****************************************************************************
290 //
291 // Switch to RCOSC_HF and turn off XOSC_HF
292 //
293 //*****************************************************************************
294 void
OSCHF_SwitchToRcOscTurnOffXosc(void)295 OSCHF_SwitchToRcOscTurnOffXosc( void )
296 {
297 #if ( defined( ROM_OSCClockSourceSet ))
298 ROM_OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_RCOSC_HF );
299 #else
300 OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_RCOSC_HF );
301 #endif
302
303 // Do the switching if not already running on RCOSC_HF
304 #if ( defined( ROM_OSCClockSourceGet ))
305 if ( ROM_OSCClockSourceGet( OSC_SRC_CLK_HF ) != OSC_RCOSC_HF )
306 #else
307 if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) != OSC_RCOSC_HF )
308 #endif
309 {
310 OSCHfSourceSwitch();
311 }
312
313 oscHfGlobals.timeXoscOff_CV = AONRTCCurrentCompareValueGet();
314 oscHfGlobals.tempXoscOff = AONBatMonTemperatureGetDegC();
315 }
316
317 //*****************************************************************************
318 //
319 // Adjust the XOSC HF cap array relative to the factory setting
320 //
321 //*****************************************************************************
322 void
OSC_AdjustXoscHfCapArray(int32_t capArrDelta)323 OSC_AdjustXoscHfCapArray( int32_t capArrDelta )
324 {
325 {
326 // Read the MODE_CONF register in CCFG
327 uint32_t ccfg_ModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF );
328 // Clear CAP_MODE and the CAPARRAY_DELATA field
329 ccfg_ModeConfReg &= ~( CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_M | CCFG_MODE_CONF_XOSC_CAP_MOD_M );
330 // Insert new delta value
331 ccfg_ModeConfReg |= ((((uint32_t)capArrDelta) << CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_S ) & CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_M );
332 // Update the HW register with the new delta value
333 DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ANABYPASSVAL1, SetupGetTrimForAnabypassValue1( ccfg_ModeConfReg ));
334 }
335 }
336
337 //*****************************************************************************
338 //
339 // Initialize the frequency offset curve fitting parameters
340 // These are calculated based on the FCFG1:HPOSC_MEAS_x parameters.
341 //
342 //*****************************************************************************
343 #define D1OFFSET_25C (-16)
344 #define D2OFFSET_85C (-23)
345 #define D3OFFSET_n40C (5)
346
347 #define HPOSC_COEFF_BITS (20) // HPOSC p1,p2,p3 coefficient precision
348 #define HPOSC_COEFF0_BITS (16) // HPOSC p0 coefficient precision
349 #define HPOSC_D_BITS (30) // HPOSC internal parameter
350 #define HPOSC_COEFF0_SHIFT (HPOSC_COEFF_BITS - HPOSC_COEFF0_BITS) // HPOSC internal parameter
351 #define HPOSC_SHIFT1 (2*HPOSC_COEFF_BITS - HPOSC_D_BITS) // HPOSC internal parameter
352 #define HPOSC_DC_BIAS (100000) // HPOSC internal parameter
353
354 int32_t _hposcCoeffs[4] = {0}; // HPOSC polynomial coefficients
355
356 typedef struct
357 {
358 int32_t temp[3];
359 int32_t dFreq[3];
360 } hposc_insertions_t;
361
362 typedef struct
363 {
364 uint8_t pu0b[4];
365 uint8_t pu1b[4];
366 uint8_t pu2b[4];
367 int64_t pu0c[4];
368 int64_t pu1c[4];
369 int64_t pu2c[4];
370 } hposc_param_t;
371
372 static void
multiplyColumns(int64_t * v1,int64_t * v2,int64_t * pBuf,uint8_t shift)373 multiplyColumns(int64_t *v1, int64_t *v2, int64_t *pBuf, uint8_t shift)
374 {
375 pBuf[0] = (v1[1]*v2[2]) >> shift;
376 pBuf[1] = (v1[2]*v2[1]) >> shift;
377 pBuf[2] = (v1[0]*v2[2]) >> shift;
378 pBuf[3] = (v1[2]*v2[0]) >> shift;
379 pBuf[4] = (v1[0]*v2[1]) >> shift;
380 pBuf[5] = (v1[1]*v2[0]) >> shift;
381 }
382
383 static int64_t
findDenominator(int64_t * col0,int64_t * col1,int64_t * col2)384 findDenominator(int64_t *col0, int64_t *col1, int64_t *col2)
385 {
386 int64_t tmp, tmpBuf[6];
387
388 multiplyColumns(col1, col2, tmpBuf, HPOSC_SHIFT1); // Keep HPOSC_D_BITS precision
389
390 tmp = (tmpBuf[0]*col0[0] - tmpBuf[1]*col0[0] - tmpBuf[2]*col0[1] +
391 tmpBuf[3]*col0[1] + tmpBuf[4]*col0[2] - tmpBuf[5]*col0[2]) >> HPOSC_COEFF_BITS;
392
393 return tmp;
394 }
395
396 static int64_t
findNumerator(int32_t * pInput,int64_t * pBuf)397 findNumerator(int32_t *pInput, int64_t *pBuf)
398 {
399 int64_t tmp;
400
401 tmp = ((int64_t)pInput[0]*pBuf[0]) - ((int64_t)pInput[0]*pBuf[1]) - ((int64_t)pInput[1]*pBuf[2]) +
402 ((int64_t)pInput[1]*pBuf[3]) + ((int64_t)pInput[2]*pBuf[4]) - ((int64_t)pInput[2]*pBuf[5]);
403
404 return tmp;
405 }
406
407 static void
findHposcCoefficients(int32_t * pInput,int64_t * col0,int64_t * col1,int64_t * col2,hposc_param_t * pParam)408 findHposcCoefficients(int32_t *pInput, int64_t *col0, int64_t *col1, int64_t *col2, hposc_param_t *pParam)
409 {
410 int64_t d,c0,c1,c2,cn,tmpBuf[6];
411 int32_t inputBuf[3];
412 uint8_t i;
413
414 if(col1 == NULL) /* 1 insertion */
415 {
416 inputBuf[0] = pInput[0] - HPOSC_DC_BIAS;
417 c0 = (((int64_t)inputBuf[0] << HPOSC_D_BITS) / col0[0]);
418 c1 = 0;
419 c2 = 0;
420 }
421 else /* 3 insertion */
422 {
423 /* Apply DC bias to input data */
424 for(i = 0; i < 3; i++)
425 {
426 inputBuf[i] = pInput[i] - HPOSC_DC_BIAS;
427 }
428
429 /* Solve intermediate parameters, d: HPOSC_D_BITS, c: HPOSC_COEFF_BITS*2 bits */
430 d = findDenominator(col0, col1, col2);
431
432 multiplyColumns(col1, col2, tmpBuf, 0);
433 cn = findNumerator(inputBuf, tmpBuf);
434 c0 = cn / d;
435
436 multiplyColumns(col0, col2, tmpBuf, 0);
437 cn = -1*findNumerator(inputBuf, tmpBuf);
438 c1 = cn / d;
439
440 multiplyColumns(col0, col1, tmpBuf, 0);
441 cn = findNumerator(inputBuf, tmpBuf);
442 c2 = cn / d;
443 }
444
445 /* Compute TCF polynomial coefficients */
446 for(i = 0; i < 4; i++)
447 {
448 cn = (((pParam->pu0c[i]*c0) >> (pParam->pu0b[i] - HPOSC_COEFF_BITS)) +
449 ((pParam->pu1c[i]*c1) >> (pParam->pu1b[i] - HPOSC_COEFF_BITS)) +
450 ((pParam->pu2c[i]*c2) >> (pParam->pu2b[i] - HPOSC_COEFF_BITS))) >> HPOSC_SHIFT1;
451
452 if(i<3)
453 {
454 _hposcCoeffs[3-i] = cn;
455 }
456 else
457 {
458 _hposcCoeffs[0] = (cn >> HPOSC_COEFF0_SHIFT) + ((int64_t)HPOSC_DC_BIAS << HPOSC_COEFF0_BITS); // p[0] is combined with the DC bias
459 }
460 }
461 }
462
463 static void
findHposcPc(int64_t * pCoeff,uint8_t * pBits,int32_t * pTemp,uint8_t nTemp,int64_t * pOutput)464 findHposcPc(int64_t *pCoeff, uint8_t *pBits, int32_t *pTemp, uint8_t nTemp, int64_t *pOutput)
465 {
466 uint8_t i;
467 int32_t t1,t2,t3;
468
469 for(i = 0; i < nTemp; i++)
470 {
471 t1 = pTemp[i];
472 t2 = t1*t1;
473 t3 = t2*t1;
474
475 pOutput[i] = (((int64_t)pCoeff[0]*t3)>>(pBits[0]-HPOSC_COEFF_BITS)) + (((int64_t)pCoeff[1]*t2)>>(pBits[1]-HPOSC_COEFF_BITS)) +
476 (((int64_t)pCoeff[2]*t1)>>(pBits[2]-HPOSC_COEFF_BITS)) + (((int64_t)pCoeff[3] )>>(pBits[3]-HPOSC_COEFF_BITS));
477 }
478 }
479
480 static void
readTempAndFreq(uint32_t regAddr,int32_t * pTemp,int32_t * pdFreq,int32_t deltaFreq)481 readTempAndFreq(uint32_t regAddr, int32_t *pTemp, int32_t *pdFreq, int32_t deltaFreq)
482 {
483 uint32_t insertionData = HWREG(regAddr);
484
485 /* temp_stored = Temperature - 27, offset by -27C */
486 *pTemp = (((int32_t)( insertionData << ( 32 - FCFG1_HPOSC_MEAS_1_HPOSC_T1_W - FCFG1_HPOSC_MEAS_1_HPOSC_T1_S )))
487 >> ( 32 - FCFG1_HPOSC_MEAS_1_HPOSC_T1_W ));
488
489 /* dFreq_stored = round( (Freq/12e6 - 1) * 2^22 ), 12MHz is the ideal frequency */
490 *pdFreq = (((int32_t)( insertionData << ( 32 - FCFG1_HPOSC_MEAS_1_HPOSC_D1_W - FCFG1_HPOSC_MEAS_1_HPOSC_D1_S )))
491 >> ( 32 - FCFG1_HPOSC_MEAS_1_HPOSC_D1_W ));
492 *pdFreq = *pdFreq + deltaFreq;
493 }
494
495 //*****************************************************************************
496 // The HPOSC initialization function
497 // - Must be called before using HPOSC
498 // - To be used when three temperature frequency offset measurements are available
499 //*****************************************************************************
500 void
OSC_HPOSCInitializeFrequencyOffsetParameters(void)501 OSC_HPOSCInitializeFrequencyOffsetParameters( void )
502 {
503 /* Initialize HPOSC internal parameter */
504 hposc_insertions_t hposcMeas;
505 int64_t pu0[3],pu1[3],pu2[3] = {0};
506 hposc_param_t hposcParm =
507 {
508 .pu0b = {42, 32, 27, 20},
509 .pu0c = {-284,-184,536,-104798},
510 .pu1b = {36, 32, 27, 20},
511 .pu1c = {-1155,44130,-319090,-3563},
512 .pu2b = {36, 32, 27, 20},
513 .pu2c = {-3410,261727,-32194,-116627}
514 };
515
516 /* Retrieve insertions from FCFG */
517 readTempAndFreq(FCFG1_BASE + FCFG1_O_HPOSC_MEAS_1, &hposcMeas.temp[0], &hposcMeas.dFreq[0], D1OFFSET_25C);
518 readTempAndFreq(FCFG1_BASE + FCFG1_O_HPOSC_MEAS_2, &hposcMeas.temp[1], &hposcMeas.dFreq[1], D2OFFSET_85C);
519 readTempAndFreq(FCFG1_BASE + FCFG1_O_HPOSC_MEAS_3, &hposcMeas.temp[2], &hposcMeas.dFreq[2], D3OFFSET_n40C);
520
521 /* Compute HPOSC polynomial coefficients */
522 findHposcPc(hposcParm.pu0c, hposcParm.pu0b, &hposcMeas.temp[0], 3, pu0);
523 findHposcPc(hposcParm.pu1c, hposcParm.pu1b, &hposcMeas.temp[0], 3, pu1);
524 findHposcPc(hposcParm.pu2c, hposcParm.pu2b, &hposcMeas.temp[0], 3, pu2);
525 findHposcCoefficients(&hposcMeas.dFreq[0], pu0, pu1, pu2, &hposcParm);
526 }
527
528 //*****************************************************************************
529 // Degub function to calculate the HPOSC polynomials for experimental data sets.
530 //*****************************************************************************
531 void
OSC_HPOSC_Debug_InitFreqOffsetParams(HposcDebugData_t * pDebugData)532 OSC_HPOSC_Debug_InitFreqOffsetParams( HposcDebugData_t * pDebugData )
533 {
534 /* Initialize HPOSC internal parameter */
535 hposc_insertions_t hposcMeas;
536 int64_t pu0[3],pu1[3],pu2[3] = {0};
537 hposc_param_t hposcParm =
538 {
539 .pu0b = {42, 32, 27, 20},
540 .pu0c = {-284,-184,536,-104798},
541 .pu1b = {36, 32, 27, 20},
542 .pu1c = {-1155,44130,-319090,-3563},
543 .pu2b = {36, 32, 27, 20},
544 .pu2c = {-3410,261727,-32194,-116627}
545 };
546
547 /* Retrieve insertions from FCFG */
548 readTempAndFreq((uint32_t)&pDebugData->meas_1, &hposcMeas.temp[0], &hposcMeas.dFreq[0], pDebugData->offsetD1);
549 readTempAndFreq((uint32_t)&pDebugData->meas_2, &hposcMeas.temp[1], &hposcMeas.dFreq[1], pDebugData->offsetD2);
550 readTempAndFreq((uint32_t)&pDebugData->meas_3, &hposcMeas.temp[2], &hposcMeas.dFreq[2], pDebugData->offsetD3);
551
552 /* Compute HPOSC polynomial coefficients */
553 findHposcPc(hposcParm.pu0c, hposcParm.pu0b, &hposcMeas.temp[0], 3, pu0);
554 findHposcPc(hposcParm.pu1c, hposcParm.pu1b, &hposcMeas.temp[0], 3, pu1);
555 findHposcPc(hposcParm.pu2c, hposcParm.pu2b, &hposcMeas.temp[0], 3, pu2);
556 findHposcCoefficients(&hposcMeas.dFreq[0], pu0, pu1, pu2, &hposcParm);
557 }
558
559 //*****************************************************************************
560 // Special HPOSC initialization function
561 // - Used when a single temperature offset measurement is available
562 // - To get a better crystal performance (SW TCXO)
563 //*****************************************************************************
564 void
OSC_HPOSCInitializeSingleInsertionFreqOffsParams(uint32_t measFieldAddress)565 OSC_HPOSCInitializeSingleInsertionFreqOffsParams( uint32_t measFieldAddress )
566 {
567 /* Initialize HPOSC internal parameter */
568 hposc_insertions_t hposcMeas;
569 int64_t pu0;
570 hposc_param_t hposcParm =
571 {
572 /* Coefficients for SW-TCXO */
573 .pu0b = {44, 44, 27, 20},
574 .pu0c = {8183, -2546, -210, -104866}
575 };
576
577 /* Retrieve insertions from FCFG */
578 readTempAndFreq( measFieldAddress, &hposcMeas.temp[0], &hposcMeas.dFreq[0], 0);
579
580 /* Compute HPOSC polynomial coefficients */
581 findHposcPc(hposcParm.pu0c, hposcParm.pu0b, &hposcMeas.temp[0], 1, &pu0);
582 findHposcCoefficients(&hposcMeas.dFreq[0], &pu0, NULL, NULL, &hposcParm);
583 }
584
585 //*****************************************************************************
586 //
587 // Calculate the temperature dependent relative frequency offset of HPOSC
588 //
589 //*****************************************************************************
590 int32_t
OSC_HPOSCRelativeFrequencyOffsetGet(int32_t tempDegC)591 OSC_HPOSCRelativeFrequencyOffsetGet( int32_t tempDegC )
592 {
593 // Estimate HPOSC frequency offset, using temperature and curve fitting parameters
594
595 // Now we can find the HPOSC freq offset, given as a signed variable d, expressed by:
596 //
597 // F_HPOSC = F_nom * (1 + d/(2^22)) , where: F_HPOSC = HPOSC frequency
598 // F_nom = nominal clock source frequency (e.g. 48.000 MHz)
599 // d = describes relative freq offset
600
601 // We can estimate the d variable, using temperature compensation parameters:
602 //
603 // d = P[3]*(t - T0)^3 + P[2]*(t - T0)^2 + P[1]*(t - T0) + P[0], where: P0,P1,P2,P3 are curve fitting parameters
604 // t = current temperature (from temp sensor) in deg C
605 // T0 = 27 deg C (fixed temperature constant)
606
607 int32_t d,t1,t2,t3;
608
609 t1 = tempDegC - 27;
610 t2 = t1 * t1;
611 t3 = t2 * t1;
612
613 d = ((((int64_t)_hposcCoeffs[3]*t3 + (int64_t)_hposcCoeffs[2]*t2 + (int64_t)_hposcCoeffs[1]*t1) >> HPOSC_COEFF0_SHIFT) +
614 (int64_t)_hposcCoeffs[0]) >> HPOSC_COEFF0_BITS;
615
616 return ( d );
617 }
618
619 //*****************************************************************************
620 //
621 // Converts the relative frequency offset of HPOSC to the RF Core parameter format.
622 //
623 //*****************************************************************************
624 int16_t
OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(int32_t HPOSC_RelFreqOffset)625 OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert( int32_t HPOSC_RelFreqOffset )
626 {
627 // The input argument, hereby referred to simply as "d", describes the frequency offset
628 // of the HPOSC relative to the nominal frequency in this way:
629 //
630 // F_HPOSC = F_nom * (1 + d/(2^22))
631 //
632 // But for use by the radio, to compensate the frequency error, we need to find the
633 // frequency offset "rfcFreqOffset" defined in the following format:
634 //
635 // F_nom = F_HPOSC * (1 + rfCoreFreqOffset/(2^22))
636 //
637 // To derive "rfCoreFreqOffset" from "d" we combine the two above equations and get:
638 //
639 // (1 + rfCoreFreqOffset/(2^22)) = (1 + d/(2^22))^-1
640 //
641 // Which can be rewritten into:
642 //
643 // rfCoreFreqOffset = -d*(2^22) / ((2^22) + d)
644 //
645 // = -d * [ 1 / (1 + d/(2^22)) ]
646 //
647 // To avoid doing a 64-bit division due to the (1 + d/(2^22))^-1 expression,
648 // we can use Taylor series (Maclaurin series) to approximate it:
649 //
650 // 1 / (1 - x) ~= 1 + x + x^2 + x^3 + x^4 + ... etc (Maclaurin series)
651 //
652 // In our case, we have x = - d/(2^22), and we only include up to the first
653 // order term of the series, as the second order term ((d^2)/(2^44)) is very small:
654 //
655 // freqError ~= -d + d^2/(2^22) (+ small approximation error)
656 //
657 // The approximation error is negligible for our use.
658
659 int32_t rfCoreFreqOffset = -HPOSC_RelFreqOffset + (( HPOSC_RelFreqOffset * HPOSC_RelFreqOffset ) >> 22 );
660
661 return ( rfCoreFreqOffset );
662 }
663
664 //*****************************************************************************
665 //
666 // Compensate the RTC increment based on the relative frequency offset of HPOSC
667 //
668 //*****************************************************************************
669 void
OSC_HPOSCRtcCompensate(int32_t relFreqOffset)670 OSC_HPOSCRtcCompensate( int32_t relFreqOffset )
671 {
672 uint32_t rtcSubSecInc;
673 uint32_t lfClkFrequency;
674 uint32_t hfFreq;
675 int64_t calcFactor;
676
677 // Calculate SCLK_HF frequency, defined as:
678 // hfFreq = 48000000 * (1 + relFreqOffset/(2^22))
679 if( relFreqOffset >= 0 )
680 {
681 calcFactor = ( ( 48000000 * (int64_t)relFreqOffset ) + 0x200000 ) / 0x400000;
682 }
683 else
684 {
685 calcFactor = ( ( 48000000 * (int64_t)relFreqOffset ) - 0x200000 ) / 0x400000;
686 }
687 hfFreq = 48000000 + calcFactor;
688
689 // Calculate SCLK_LF frequency, defined as SCLK_LF_FREQ = SCLK_HF_FREQ / 1536
690 lfClkFrequency = ( hfFreq + 768 ) / 1536;
691
692 // Calculate SUBSECINC, defined as: SUBSECINC = 2^38 / SCLK_LF_FREQ
693 rtcSubSecInc = 0x4000000000 / lfClkFrequency;
694
695 /* Update SUBSECINC value */
696 SetupSetAonRtcSubSecInc(rtcSubSecInc);
697 }
698
699 //*****************************************************************************
700 //
701 // Get crystal amplitude (assuming crystal is running).
702 //
703 //*****************************************************************************
704 uint32_t
OSCHF_DebugGetCrystalAmplitude(void)705 OSCHF_DebugGetCrystalAmplitude( void )
706 {
707 uint32_t oscCfgRegCopy ;
708 uint32_t startTime ;
709 uint32_t deltaTime ;
710 uint32_t ampValue ;
711
712 // The specified method is as follows:
713 // 1. Set minimum interval between oscillator amplitude calibrations.
714 // (Done by setting PER_M=0 and PER_E=1)
715 // 2. Wait approximately 4 milliseconds in order to measure over a
716 // moderately large number of calibrations.
717 // 3. Read out the crystal amplitude value from the peek detector.
718 // 4. Restore original oscillator amplitude calibrations interval.
719 // 5. Return crystal amplitude value converted to millivolt.
720 oscCfgRegCopy = HWREG( AON_PMCTL_BASE + AON_PMCTL_O_OSCCFG );
721 HWREG( AON_PMCTL_BASE + AON_PMCTL_O_OSCCFG ) = ( 1 << AON_PMCTL_OSCCFG_PER_E_S );
722 startTime = AONRTCCurrentCompareValueGet();
723 do {
724 deltaTime = AONRTCCurrentCompareValueGet() - startTime;
725 } while ( deltaTime < ((uint32_t)( 0.004 * FACTOR_SEC_TO_COMP_VAL_FORMAT )));
726 ampValue = ( HWREG( AUX_DDI0_OSC_BASE + DDI_0_OSC_O_STAT1 ) &
727 DDI_0_OSC_STAT1_HPM_UPDATE_AMP_M ) >>
728 DDI_0_OSC_STAT1_HPM_UPDATE_AMP_S ;
729 HWREG( AON_PMCTL_BASE + AON_PMCTL_O_OSCCFG ) = oscCfgRegCopy;
730
731 return ( ampValue * 15 );
732 }
733
734 //*****************************************************************************
735 //
736 // Get the expected average crystal amplitude.
737 //
738 //*****************************************************************************
739 uint32_t
OSCHF_DebugGetExpectedAverageCrystalAmplitude(void)740 OSCHF_DebugGetExpectedAverageCrystalAmplitude( void )
741 {
742 uint32_t ampCompTh1 ;
743 uint32_t highThreshold ;
744 uint32_t lowThreshold ;
745
746 ampCompTh1 = HWREG( AUX_DDI0_OSC_BASE + DDI_0_OSC_O_AMPCOMPTH1 );
747 highThreshold = ( ampCompTh1 & DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_HTH_M ) >>
748 DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_HTH_S ;
749 lowThreshold = ( ampCompTh1 & DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_LTH_M ) >>
750 DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_LTH_S ;
751
752 return ((( highThreshold + lowThreshold ) * 15 ) >> 1 );
753 }
754
755 //*****************************************************************************
756 //
757 // Measure the crystal startup time - in number of LF clock edges
758 //
759 //*****************************************************************************
OSCHF_DebugGetCrystalStartupTime(void)760 uint32_t OSCHF_DebugGetCrystalStartupTime( void )
761 {
762 uint32_t lfEdgesFound = 0 ;
763
764 // Start operation in sync with the LF clock
765 HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );
766
767 OSCHF_TurnOnXosc();
768 while ( ! OSCHF_AttemptToSwitchToXosc() ) {
769 HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );
770 lfEdgesFound ++ ;
771 }
772 OSCHF_SwitchToRcOscTurnOffXosc();
773
774 return ( lfEdgesFound );
775 }
776