1 /******************************************************************************
2 * Filename: osc.c
3 * Revised: 2020-12-11 09:58:05 +0100 (Fri, 11 Dec 2020)
4 * Revision: 59848
5 *
6 * Description: Driver for setting up the system Oscillators
7 *
8 * Copyright (c) 2015 - 2020, Texas Instruments Incorporated
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
13 *
14 * 1) Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 *
17 * 2) Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 *
21 * 3) Neither the name of the ORGANIZATION nor the names of its contributors may
22 * be used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 ******************************************************************************/
38
39 #include <stdlib.h>
40 #include "../inc/hw_types.h"
41 #include "../inc/hw_ccfg.h"
42 #include "../inc/hw_fcfg1.h"
43 #include "aon_batmon.h"
44 #include "aon_rtc.h"
45 #include "osc.h"
46 #include "sys_ctrl.h"
47 #include "setup_rom.h"
48
49 //*****************************************************************************
50 //
51 // Handle support for DriverLib in ROM:
52 // This section will undo prototype renaming made in the header file
53 //
54 //*****************************************************************************
55 #if !defined(DOXYGEN)
56 #undef OSCClockSourceSet
57 #define OSCClockSourceSet NOROM_OSCClockSourceSet
58 #undef OSCClockSourceGet
59 #define OSCClockSourceGet NOROM_OSCClockSourceGet
60 #undef OSCHF_GetStartupTime
61 #define OSCHF_GetStartupTime NOROM_OSCHF_GetStartupTime
62 #undef OSCHF_TurnOnXosc
63 #define OSCHF_TurnOnXosc NOROM_OSCHF_TurnOnXosc
64 #undef OSCHF_AttemptToSwitchToXosc
65 #define OSCHF_AttemptToSwitchToXosc NOROM_OSCHF_AttemptToSwitchToXosc
66 #undef OSCHF_SwitchToRcOscTurnOffXosc
67 #define OSCHF_SwitchToRcOscTurnOffXosc NOROM_OSCHF_SwitchToRcOscTurnOffXosc
68 #undef OSCHF_DebugGetCrystalAmplitude
69 #define OSCHF_DebugGetCrystalAmplitude NOROM_OSCHF_DebugGetCrystalAmplitude
70 #undef OSCHF_DebugGetExpectedAverageCrystalAmplitude
71 #define OSCHF_DebugGetExpectedAverageCrystalAmplitude NOROM_OSCHF_DebugGetExpectedAverageCrystalAmplitude
72 #undef OSCHF_DebugGetCrystalStartupTime
73 #define OSCHF_DebugGetCrystalStartupTime NOROM_OSCHF_DebugGetCrystalStartupTime
74 #undef OSC_HPOSCInitializeFrequencyOffsetParameters
75 #define OSC_HPOSCInitializeFrequencyOffsetParameters NOROM_OSC_HPOSCInitializeFrequencyOffsetParameters
76 #undef OSC_HPOSC_Debug_InitFreqOffsetParams
77 #define OSC_HPOSC_Debug_InitFreqOffsetParams NOROM_OSC_HPOSC_Debug_InitFreqOffsetParams
78 #undef OSC_HPOSCInitializeSingleInsertionFreqOffsParams
79 #define OSC_HPOSCInitializeSingleInsertionFreqOffsParams NOROM_OSC_HPOSCInitializeSingleInsertionFreqOffsParams
80 #undef OSC_HPOSCRelativeFrequencyOffsetGet
81 #define OSC_HPOSCRelativeFrequencyOffsetGet NOROM_OSC_HPOSCRelativeFrequencyOffsetGet
82 #undef OSC_AdjustXoscHfCapArray
83 #define OSC_AdjustXoscHfCapArray NOROM_OSC_AdjustXoscHfCapArray
84 #undef OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
85 #define OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert NOROM_OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
86 #undef OSC_HPOSCRtcCompensate
87 #define OSC_HPOSCRtcCompensate NOROM_OSC_HPOSCRtcCompensate
88 #endif
89
90 //*****************************************************************************
91 //
92 // OSCHF switch time calculator defines and globals
93 //
94 //*****************************************************************************
95
96 #define RTC_CV_TO_MS(x) (( 1000 * ( x )) >> 16 )
97 #define RTC_CV_TO_US(x) (( 1000000 * ( x )) >> 16 )
98
99 typedef struct {
100 uint32_t previousStartupTimeInUs ;
101 uint32_t timeXoscOff_CV ;
102 uint32_t timeXoscOn_CV ;
103 uint32_t timeXoscStable_CV ;
104 int32_t tempXoscOff ;
105 } OscHfGlobals_t;
106
107 static OscHfGlobals_t oscHfGlobals;
108
109 //*****************************************************************************
110 //
111 // Configure the oscillator input to the a source clock.
112 //
113 //*****************************************************************************
114 void
OSCClockSourceSet(uint32_t ui32SrcClk,uint32_t ui32Osc)115 OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc)
116 {
117 // Check the arguments.
118 ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
119 (ui32SrcClk & OSC_SRC_CLK_HF));
120 ASSERT((ui32Osc == OSC_RCOSC_HF) ||
121 (ui32Osc == OSC_RCOSC_LF) ||
122 (ui32Osc == OSC_XOSC_HF) ||
123 (ui32Osc == OSC_XOSC_LF));
124
125 // Request the high frequency source clock (using 24 MHz XTAL)
126 if(ui32SrcClk & OSC_SRC_CLK_HF)
127 {
128 // Enable the HF XTAL as HF clock source
129 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
130 DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_M,
131 DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_S,
132 ui32Osc);
133 }
134
135 // Configure the low frequency source clock.
136 if(ui32SrcClk & OSC_SRC_CLK_LF)
137 {
138 // Change the clock source.
139 DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
140 DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_M,
141 DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_S,
142 ui32Osc);
143 }
144 }
145
146 //*****************************************************************************
147 //
148 // Get the source clock settings
149 //
150 //*****************************************************************************
151 uint32_t
OSCClockSourceGet(uint32_t ui32SrcClk)152 OSCClockSourceGet(uint32_t ui32SrcClk)
153 {
154 uint32_t ui32ClockSource;
155
156 // Check the arguments.
157 ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
158 (ui32SrcClk & OSC_SRC_CLK_HF));
159
160 // Return the source for the selected clock.
161 if(ui32SrcClk == OSC_SRC_CLK_LF)
162 {
163 ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
164 DDI_0_OSC_STAT0_SCLK_LF_SRC_M,
165 DDI_0_OSC_STAT0_SCLK_LF_SRC_S);
166 }
167 else
168 {
169 ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
170 DDI_0_OSC_STAT0_SCLK_HF_SRC_M,
171 DDI_0_OSC_STAT0_SCLK_HF_SRC_S);
172 }
173 return (ui32ClockSource);
174 }
175
176 //*****************************************************************************
177 //
178 // Returns maximum startup time (in microseconds) of XOSC_HF
179 //
180 //*****************************************************************************
181 uint32_t
OSCHF_GetStartupTime(uint32_t timeUntilWakeupInMs)182 OSCHF_GetStartupTime( uint32_t timeUntilWakeupInMs )
183 {
184 uint32_t deltaTimeSinceXoscOnInMs ;
185 int32_t deltaTempSinceXoscOn ;
186 uint32_t newStartupTimeInUs ;
187
188 // Check CCFG to determine if device is configured for TCXO.
189 if( ( HWREG( CCFG_BASE + CCFG_O_MODE_CONF ) & CCFG_MODE_CONF_XOSC_FREQ_M ) == CCFG_MODE_CONF_XOSC_FREQ_TCXO )
190 {
191 // Device configured for TCXO. Report fixed startup time located in CCFG with
192 // coversion from number of 100us to number of us.
193 newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) & CCFG_MODE_CONF_1_TCXO_MAX_START_M ) >>
194 CCFG_MODE_CONF_1_TCXO_MAX_START_S ) * 100;
195 }
196 else
197 {
198 deltaTimeSinceXoscOnInMs = RTC_CV_TO_MS( AONRTCCurrentCompareValueGet() - oscHfGlobals.timeXoscOn_CV );
199 deltaTempSinceXoscOn = AONBatMonTemperatureGetDegC() - oscHfGlobals.tempXoscOff;
200
201 if ( deltaTempSinceXoscOn < 0 ) {
202 deltaTempSinceXoscOn = -deltaTempSinceXoscOn;
203 }
204
205 if ( (( timeUntilWakeupInMs + deltaTimeSinceXoscOnInMs ) > 3000 ) ||
206 ( deltaTempSinceXoscOn > 5 ) ||
207 ( oscHfGlobals.timeXoscStable_CV < oscHfGlobals.timeXoscOn_CV ) ||
208 ( oscHfGlobals.previousStartupTimeInUs == 0 ) )
209 {
210 newStartupTimeInUs = 2000;
211 if (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_XOSC_OVR_M ) == 0 ) {
212 newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
213 CCFG_MODE_CONF_1_XOSC_MAX_START_M ) >>
214 CCFG_MODE_CONF_1_XOSC_MAX_START_S ) * 125;
215 // Note: CCFG startup time is "in units of 100us" adding 25% margin results in *125
216 }
217 } else {
218 newStartupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
219 newStartupTimeInUs += ( newStartupTimeInUs >> 2 ); // Add 25 percent margin
220 if ( newStartupTimeInUs < oscHfGlobals.previousStartupTimeInUs ) {
221 newStartupTimeInUs = oscHfGlobals.previousStartupTimeInUs;
222 }
223 }
224
225 if ( newStartupTimeInUs < 200 ) {
226 newStartupTimeInUs = 200;
227 }
228 if ( newStartupTimeInUs > 4000 ) {
229 newStartupTimeInUs = 4000;
230 }
231 }
232 return ( newStartupTimeInUs );
233 }
234
235
236 //*****************************************************************************
237 //
238 // Turns on XOSC_HF (but without switching to XOSC_HF)
239 //
240 //*****************************************************************************
241 void
OSCHF_TurnOnXosc(void)242 OSCHF_TurnOnXosc( void )
243 {
244 #if ( defined( ROM_OSCClockSourceSet ))
245 ROM_OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_XOSC_HF );
246 #else
247 OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_XOSC_HF );
248 #endif
249 oscHfGlobals.timeXoscOn_CV = AONRTCCurrentCompareValueGet();
250 }
251
252
253 //*****************************************************************************
254 //
255 // Switch to XOSC_HF if XOSC_HF is ready.
256 //
257 //*****************************************************************************
258 bool
OSCHF_AttemptToSwitchToXosc(void)259 OSCHF_AttemptToSwitchToXosc( void )
260 {
261 uint32_t startupTimeInUs;
262 uint32_t prevLimmit25InUs;
263
264 #if ( defined( ROM_OSCClockSourceGet ))
265 if ( ROM_OSCClockSourceGet( OSC_SRC_CLK_HF ) == OSC_XOSC_HF )
266 #else
267 if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) == OSC_XOSC_HF )
268 #endif
269 {
270 // Already on XOSC - nothing to do
271 return ( 1 );
272 }
273 if ( OSCHfSourceReady()) {
274 OSCHfSourceSwitch();
275
276 // Store startup time, but limit to 25 percent reduction each time.
277 oscHfGlobals.timeXoscStable_CV = AONRTCCurrentCompareValueGet();
278 startupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
279 prevLimmit25InUs = oscHfGlobals.previousStartupTimeInUs;
280 prevLimmit25InUs -= ( prevLimmit25InUs >> 2 ); // 25 percent margin
281 oscHfGlobals.previousStartupTimeInUs = startupTimeInUs;
282 if ( prevLimmit25InUs > startupTimeInUs ) {
283 oscHfGlobals.previousStartupTimeInUs = prevLimmit25InUs;
284 }
285 return ( 1 );
286 }
287 return ( 0 );
288 }
289
290
291 //*****************************************************************************
292 //
293 // Switch to RCOSC_HF and turn off XOSC_HF
294 //
295 //*****************************************************************************
296 void
OSCHF_SwitchToRcOscTurnOffXosc(void)297 OSCHF_SwitchToRcOscTurnOffXosc( void )
298 {
299 #if ( defined( ROM_OSCClockSourceSet ))
300 ROM_OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_RCOSC_HF );
301 #else
302 OSCClockSourceSet( OSC_SRC_CLK_HF, OSC_RCOSC_HF );
303 #endif
304
305 // Do the switching if not already running on RCOSC_HF
306 #if ( defined( ROM_OSCClockSourceGet ))
307 if ( ROM_OSCClockSourceGet( OSC_SRC_CLK_HF ) != OSC_RCOSC_HF )
308 #else
309 if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) != OSC_RCOSC_HF )
310 #endif
311 {
312 OSCHfSourceSwitch();
313 }
314
315 oscHfGlobals.timeXoscOff_CV = AONRTCCurrentCompareValueGet();
316 oscHfGlobals.tempXoscOff = AONBatMonTemperatureGetDegC();
317 }
318
319 //*****************************************************************************
320 //
321 // Adjust the XOSC HF cap array relative to the factory setting
322 //
323 //*****************************************************************************
324 void
OSC_AdjustXoscHfCapArray(int32_t capArrDelta)325 OSC_AdjustXoscHfCapArray( int32_t capArrDelta )
326 {
327 // read the MODE_CONF register in CCFG
328 uint32_t ccfg_ModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF );
329 // Clear CAP_MODE and the CAPARRAY_DELATA field
330 ccfg_ModeConfReg &= ~( CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_M | CCFG_MODE_CONF_XOSC_CAP_MOD_M );
331 // Insert new delta value
332 ccfg_ModeConfReg |= ((((uint32_t)capArrDelta) << CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_S ) & CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_M );
333 // Update the HW register with the new delta value
334 DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ANABYPASSVAL1, SetupGetTrimForAnabypassValue1( ccfg_ModeConfReg ));
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 = {7322, -5021, -209, -104861}
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 OSCHF_TurnOnXosc();
767 while ( ! OSCHF_AttemptToSwitchToXosc() ) {
768 HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );
769 lfEdgesFound ++ ;
770 }
771 OSCHF_SwitchToRcOscTurnOffXosc();
772
773 return ( lfEdgesFound );
774 }
775