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