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