1 /******************************************************************************
2 *  Filename:       osc.c
3 *  Revised:        2015-11-18 16:59:03 +0100 (Wed, 18 Nov 2015)
4 *  Revision:       45131
5 *
6 *  Description:    Driver for setting up the system Oscillators
7 *
8 *  Copyright (c) 2015, 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 <inc/hw_types.h>
40 #include <inc/hw_ccfg.h>
41 #include <inc/hw_fcfg1.h>
42 #include <driverlib/aon_batmon.h>
43 #include <driverlib/aon_rtc.h>
44 #include <driverlib/osc.h>
45 
46 //*****************************************************************************
47 //
48 // Handle support for DriverLib in ROM:
49 // This section will undo prototype renaming made in the header file
50 //
51 //*****************************************************************************
52 #if !defined(DOXYGEN)
53     #undef  OSCClockSourceSet
54     #define OSCClockSourceSet               NOROM_OSCClockSourceSet
55     #undef  OSCClockSourceGet
56     #define OSCClockSourceGet               NOROM_OSCClockSourceGet
57     #undef  OSCInterfaceEnable
58     #define OSCInterfaceEnable              NOROM_OSCInterfaceEnable
59 #endif
60 
61 //*****************************************************************************
62 //
63 // OSCHF switch time calculator defines and globals
64 //
65 //*****************************************************************************
66 
67 #define RTC_CV_TO_MS(x) ((    1000 * ( x )) >> 16 )
68 #define RTC_CV_TO_US(x) (( 1000000 * ( x )) >> 16 )
69 
70 typedef struct {
71    uint32_t    previousStartupTimeInUs ;
72    uint32_t    timeXoscOff_CV          ;
73    uint32_t    timeXoscOn_CV           ;
74    uint32_t    timeXoscStable_CV       ;
75    int32_t     tempXoscOff             ;
76 } OscHfGlobals_t;
77 
78 static OscHfGlobals_t oscHfGlobals;
79 
80 //*****************************************************************************
81 //
82 //  Configure the oscillator input to the a source clock.
83 //
84 //*****************************************************************************
85 void
OSCClockSourceSet(uint32_t ui32SrcClk,uint32_t ui32Osc)86 OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc)
87 {
88     //
89     // Check the arguments.
90     //
91     ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
92            (ui32SrcClk & OSC_SRC_CLK_MF) ||
93            (ui32SrcClk & OSC_SRC_CLK_HF));
94     ASSERT((ui32Osc == OSC_RCOSC_HF) ||
95            (ui32Osc == OSC_RCOSC_LF) ||
96            (ui32Osc == OSC_XOSC_HF) ||
97            (ui32Osc == OSC_XOSC_LF));
98 
99     //
100     // Request the high frequency source clock (using 24 MHz XTAL)
101     //
102     if(ui32SrcClk & OSC_SRC_CLK_HF)
103     {
104         //
105         // Enable the HF XTAL as HF clock source
106         //
107         DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
108                            DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_M,
109                            DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_S,
110                            ui32Osc);
111     }
112 
113     //
114     // Configure the medium frequency source clock
115     //
116     if(ui32SrcClk & OSC_SRC_CLK_MF)
117     {
118         DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
119                            DDI_0_OSC_CTL0_SCLK_MF_SRC_SEL_M,
120                            DDI_0_OSC_CTL0_SCLK_MF_SRC_SEL_S,
121                            ui32Osc);
122     }
123 
124     //
125     // Configure the low frequency source clock.
126     //
127     if(ui32SrcClk & OSC_SRC_CLK_LF)
128     {
129         //
130         // Change the clock source.
131         //
132         DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
133                            DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_M,
134                            DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_S,
135                            ui32Osc);
136     }
137 }
138 
139 //*****************************************************************************
140 //
141 //  Get the source clock settings
142 //
143 //*****************************************************************************
144 uint32_t
OSCClockSourceGet(uint32_t ui32SrcClk)145 OSCClockSourceGet(uint32_t ui32SrcClk)
146 {
147     uint32_t ui32ClockSource;
148 
149     //
150     // Check the arguments.
151     //
152     ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
153            (ui32SrcClk & OSC_SRC_CLK_HF));
154 
155     //
156     // Return the source for the selected clock.
157     //
158     if(ui32SrcClk == OSC_SRC_CLK_LF)
159     {
160         ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
161                                             DDI_0_OSC_STAT0_SCLK_LF_SRC_M,
162                                             DDI_0_OSC_STAT0_SCLK_LF_SRC_S);
163     }
164     else
165     {
166         ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
167                                             DDI_0_OSC_STAT0_SCLK_HF_SRC_M,
168                                             DDI_0_OSC_STAT0_SCLK_HF_SRC_S);
169     }
170     return (ui32ClockSource);
171 }
172 
173 //*****************************************************************************
174 //
175 // Enable System CPU access to the OSC_DIG module
176 //
177 //*****************************************************************************
178 void
OSCInterfaceEnable(void)179 OSCInterfaceEnable(void)
180 {
181     //
182     // Force power on AUX to ensure CPU has access
183     //
184     AONWUCAuxWakeupEvent(AONWUC_AUX_WAKEUP);
185     while(!(AONWUCPowerStatusGet() & AONWUC_AUX_POWER_ON))
186     { }
187 
188     //
189     // Enable the AUX domain OSC clock and wait for it to be ready
190     //
191     AUXWUCClockEnable(AUX_WUC_OSCCTRL_CLOCK);
192     while(AUXWUCClockStatus(AUX_WUC_OSCCTRL_CLOCK) != AUX_WUC_CLOCK_READY)
193     { }
194 }
195 
196 
197 //*****************************************************************************
198 //
199 // Returns maximum startup time (in microseconds) of XOSC_HF
200 //
201 //*****************************************************************************
202 uint32_t
OSCHF_GetStartupTime(uint32_t timeUntilWakeupInMs)203 OSCHF_GetStartupTime( uint32_t timeUntilWakeupInMs )
204 {
205    uint32_t deltaTimeSinceXoscOnInMs   ;
206    int32_t  deltaTempSinceXoscOn       ;
207    uint32_t newStartupTimeInUs         ;
208 
209    deltaTimeSinceXoscOnInMs = RTC_CV_TO_MS( AONRTCCurrentCompareValueGet() - oscHfGlobals.timeXoscOn_CV );
210    deltaTempSinceXoscOn     = AONBatMonTemperatureGetDegC() - oscHfGlobals.tempXoscOff;
211 
212    if ( deltaTempSinceXoscOn < 0 ) {
213       deltaTempSinceXoscOn = -deltaTempSinceXoscOn;
214    }
215 
216    if (  (( timeUntilWakeupInMs + deltaTimeSinceXoscOnInMs )     > 3000 ) ||
217          ( deltaTempSinceXoscOn                                  >    5 ) ||
218          ( oscHfGlobals.timeXoscStable_CV < oscHfGlobals.timeXoscOn_CV  ) ||
219          ( oscHfGlobals.previousStartupTimeInUs                  ==   0 )    )
220    {
221       newStartupTimeInUs = 2000;
222       if (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_XOSC_OVR_M ) == 0 ) {
223          newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
224             CCFG_MODE_CONF_1_XOSC_MAX_START_M ) >>
225             CCFG_MODE_CONF_1_XOSC_MAX_START_S ) * 125;
226             // Note: CCFG startup time is "in units of 100us" adding 25% margin results in *125
227       }
228    } else {
229       newStartupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
230       newStartupTimeInUs += ( newStartupTimeInUs >> 2 ); // Add 25 percent margin
231       if ( newStartupTimeInUs < oscHfGlobals.previousStartupTimeInUs ) {
232          newStartupTimeInUs = oscHfGlobals.previousStartupTimeInUs;
233       }
234    }
235 
236    if ( newStartupTimeInUs < 200 ) {
237       newStartupTimeInUs = 200;
238    }
239    if ( newStartupTimeInUs > 4000 ) {
240       newStartupTimeInUs = 4000;
241    }
242    return ( newStartupTimeInUs );
243 }
244 
245 
246 //*****************************************************************************
247 //
248 // Turns on XOSC_HF (but without switching to XOSC_HF)
249 //
250 //*****************************************************************************
251 void
OSCHF_TurnOnXosc(void)252 OSCHF_TurnOnXosc( void )
253 {
254    OSCClockSourceSet( OSC_SRC_CLK_HF | OSC_SRC_CLK_MF, OSC_XOSC_HF );
255    oscHfGlobals.timeXoscOn_CV  = AONRTCCurrentCompareValueGet();
256 }
257 
258 
259 //*****************************************************************************
260 //
261 // Switch to XOSC_HF if XOSC_HF is ready.
262 //
263 //*****************************************************************************
264 bool
OSCHF_AttemptToSwitchToXosc(void)265 OSCHF_AttemptToSwitchToXosc( void )
266 {
267    uint32_t startupTimeInUs;
268    uint32_t prevLimmit25InUs;
269 
270    if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) == OSC_XOSC_HF ) {
271       // Already on XOSC - nothing to do
272       return ( 1 );
273    }
274    if ( OSCHfSourceReady()) {
275       OSCHfSourceSwitch();
276 
277       //
278       // Store startup time, but limit to 25 percent reduction each time.
279       //
280       oscHfGlobals.timeXoscStable_CV  = AONRTCCurrentCompareValueGet();
281       startupTimeInUs   = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
282       prevLimmit25InUs  = oscHfGlobals.previousStartupTimeInUs;
283       prevLimmit25InUs -= ( prevLimmit25InUs >> 2 ); // 25 percent margin
284       oscHfGlobals.previousStartupTimeInUs = startupTimeInUs;
285       if ( prevLimmit25InUs > startupTimeInUs ) {
286          oscHfGlobals.previousStartupTimeInUs = prevLimmit25InUs;
287       }
288       return ( 1 );
289    }
290    return ( 0 );
291 }
292 
293 
294 //*****************************************************************************
295 //
296 // Switch to RCOSC_HF and turn off XOSC_HF
297 //
298 //*****************************************************************************
299 void
OSCHF_SwitchToRcOscTurnOffXosc(void)300 OSCHF_SwitchToRcOscTurnOffXosc( void )
301 {
302    //
303    // Set SCLK_HF and SCLK_MF to RCOSC_HF without checking
304    // Doing this anyway to keep HF and MF in sync
305    //
306    OSCClockSourceSet( OSC_SRC_CLK_HF | OSC_SRC_CLK_MF, OSC_RCOSC_HF );
307 
308    //
309    // Do the switching if not already running on RCOSC_HF
310    //
311    if ( OSCClockSourceGet( OSC_SRC_CLK_HF ) != OSC_RCOSC_HF ) {
312       OSCHfSourceSwitch();
313    }
314 
315    oscHfGlobals.timeXoscOff_CV  = AONRTCCurrentCompareValueGet();
316    oscHfGlobals.tempXoscOff     = AONBatMonTemperatureGetDegC();
317 }
318 
319 //*****************************************************************************
320 //
321 // Calculate the temperature dependent relative frequency offset of HPOSC
322 //
323 //*****************************************************************************
324 int32_t
OSC_HPOSCRelativeFrequencyOffsetGet(int32_t tempDegC)325 OSC_HPOSCRelativeFrequencyOffsetGet( int32_t tempDegC )
326 {
327    // Estimate HPOSC frequency, using temperature and curve fitting parameters
328    uint32_t fitParams = HWREG(FCFG1_BASE + FCFG1_O_FREQ_OFFSET);
329    // Extract the P0,P1,P2 params, and sign extend them via shifting up/down
330    int32_t paramP0 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_S))
331                                              >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W));
332    int32_t paramP1 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_S))
333                                              >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W));
334    int32_t paramP2 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_S))
335                                              >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W));
336    int32_t paramP3 = ((((int32_t) HWREG(FCFG1_BASE + FCFG1_O_MISC_CONF_2))
337                                              << (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_S))
338                                              >> (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W));
339 
340    // Now we can find the HPOSC freq offset, given as a signed variable d, expressed by:
341    //
342    //    F_HPOSC = F_nom * (1 + d/(2^22))    , where: F_HPOSC = HPOSC frequency
343    //                                                 F_nom = nominal clock source frequency (e.g. 48.000 MHz)
344    //                                                 d = describes relative freq offset
345 
346    // We can estimate the d variable, using temperature compensation parameters:
347    //
348    //    d = P0 + P1*(t - T0) + P2*(t - T0)^2 + P3*(t - T0)^3, where: P0,P1,P2,P3 are curve fitting parameters from FCFG1
349    //                                                 t = current temperature (from temp sensor) in deg C
350    //                                                 T0 = 27 deg C (fixed temperature constant)
351    int32_t tempDelta = (tempDegC - 27);
352    int32_t tempDeltaX2 = tempDelta * tempDelta;
353    int32_t d = paramP0 + ((tempDelta*paramP1)>>3) + ((tempDeltaX2*paramP2)>>10) + ((tempDeltaX2*tempDelta*paramP3)>>18);
354 
355    return ( d );
356 }
357 
358 //*****************************************************************************
359 //
360 // Converts the relative frequency offset of HPOSC to the RF Core parameter format.
361 //
362 //*****************************************************************************
363 int16_t
OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(int32_t HPOSC_RelFreqOffset)364 OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert( int32_t HPOSC_RelFreqOffset )
365 {
366    // The input argument, hereby referred to simply as "d", describes the frequency offset
367    // of the HPOSC relative to the nominal frequency in this way:
368    //
369    //    F_HPOSC = F_nom * (1 + d/(2^22))
370    //
371    // But for use by the radio, to compensate the frequency error, we need to find the
372    // frequency offset "rfcFreqOffset" defined in the following format:
373    //
374    //    F_nom = F_HPOSC * (1 + rfCoreFreqOffset/(2^22))
375    //
376    // To derive "rfCoreFreqOffset" from "d" we combine the two above equations and get:
377    //
378    //    (1 + rfCoreFreqOffset/(2^22)) = (1 + d/(2^22))^-1
379    //
380    // Which can be rewritten into:
381    //
382    //    rfCoreFreqOffset = -d*(2^22) / ((2^22) + d)
383    //
384    //               = -d * [ 1 / (1 + d/(2^22)) ]
385    //
386    // To avoid doing a 64-bit division due to the (1 + d/(2^22))^-1 expression,
387    // we can use Taylor series (Maclaurin series) to approximate it:
388    //
389    //       1 / (1 - x) ~= 1 + x + x^2 + x^3 + x^4 + ... etc      (Maclaurin series)
390    //
391    // In our case, we have x = - d/(2^22), and we only include up to the first
392    // order term of the series, as the second order term ((d^2)/(2^44)) is very small:
393    //
394    //       freqError ~= -d + d^2/(2^22)   (+ small approximation error)
395    //
396    // The approximation error is negligible for our use.
397 
398    int32_t rfCoreFreqOffset = -HPOSC_RelFreqOffset + (( HPOSC_RelFreqOffset * HPOSC_RelFreqOffset ) >> 22 );
399 
400    return ( rfCoreFreqOffset );
401 }
402