1 /*
2 * Copyright (c) 2015-2020, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== PowerCC26X2.c ========
34 */
35
36 #include <stdbool.h>
37
38 #include <ti/drivers/dpl/HwiP.h>
39 #include <ti/drivers/dpl/ClockP.h>
40 #include <ti/drivers/dpl/SwiP.h>
41 #include <ti/drivers/dpl/DebugP.h>
42
43 #include <ti/drivers/Power.h>
44 #include <ti/drivers/power/PowerCC26X2.h>
45 #include <ti/drivers/Temperature.h>
46
47 /* driverlib header files */
48 #include <ti/devices/DeviceFamily.h>
49 #include DeviceFamily_constructPath(inc/hw_types.h)
50 #include DeviceFamily_constructPath(inc/hw_prcm.h)
51 #include DeviceFamily_constructPath(inc/hw_nvic.h)
52 #include DeviceFamily_constructPath(inc/hw_aux_sysif.h)
53 #include DeviceFamily_constructPath(inc/hw_aon_rtc.h)
54 #include DeviceFamily_constructPath(inc/hw_memmap.h)
55 #include DeviceFamily_constructPath(inc/hw_ccfg.h)
56 #include DeviceFamily_constructPath(inc/hw_rfc_pwr.h)
57 #include DeviceFamily_constructPath(inc/hw_aon_pmctl.h)
58 #include DeviceFamily_constructPath(inc/hw_fcfg1.h)
59 #include DeviceFamily_constructPath(inc/hw_ints.h)
60 #include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
61 #include DeviceFamily_constructPath(driverlib/pwr_ctrl.h)
62 #include DeviceFamily_constructPath(driverlib/prcm.h)
63 #include DeviceFamily_constructPath(driverlib/aon_ioc.h)
64 #include DeviceFamily_constructPath(driverlib/aon_rtc.h)
65 #include DeviceFamily_constructPath(driverlib/aon_event.h)
66 #include DeviceFamily_constructPath(driverlib/osc.h)
67 #include DeviceFamily_constructPath(driverlib/cpu.h)
68 #include DeviceFamily_constructPath(driverlib/vims.h)
69 #include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
70 #include DeviceFamily_constructPath(driverlib/driverlib_release.h)
71 #include DeviceFamily_constructPath(driverlib/setup.h)
72 #include DeviceFamily_constructPath(driverlib/ccfgread.h)
73
74 static unsigned int configureXOSCHF(unsigned int action);
75 static unsigned int nopResourceHandler(unsigned int action);
76 static unsigned int configureRFCoreClocks(unsigned int action);
77 static void switchXOSCHF(void);
78 static void disableLFClockQualifiers(void);
79 static void emptyClockFunc(uintptr_t arg);
80 static int_fast16_t notify(uint_fast16_t eventType);
81 static void oscillatorISR(uintptr_t arg);
82 static void switchToTCXO(void);
83 static void delayUs(uint32_t us);
84 static void hposcRtcCompensateFxn(int16_t currentTemperature,
85 int16_t thresholdTemperature,
86 uintptr_t clientArg,
87 Temperature_NotifyObj *notifyObject);
88
89 /* RCOSC calibration functions functions */
90 extern void PowerCC26X2_calibrate(void);
91 extern bool PowerCC26X2_initiateCalibration(void);
92 extern void PowerCC26X2_auxISR(uintptr_t arg);
93 extern void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg);
94
95 /* Externs */
96 extern const PowerCC26X2_Config PowerCC26X2_config;
97
98 /* Module_State */
99 PowerCC26X2_ModuleState PowerCC26X2_module = {
100 .notifyList = {0}, /* list of registered notifications */
101 .constraintMask = 0, /* the constraint mask */
102 .clockObj = {0}, /* Clock object for scheduling wakeups */
103 .calibrationClock = {0}, /* Clock object for RCOSC calibration */
104 .tcxoEnableClock = {0}, /* Clock object for TCXO startup */
105 .tdcHwi = {0}, /* hwi object for calibration */
106 .oscHwi = {0}, /* hwi object for oscillators */
107 .hposcRtcCompNotifyObj = {{0}}, /* Temperature notification */
108 .nDeltaFreqCurr = 0, /* RCOSC calibration variable */
109 .nCtrimCurr = 0, /* RCOSC calibration variable */
110 .nCtrimFractCurr = 0, /* RCOSC calibration variable */
111 .nCtrimNew = 0, /* RCOSC calibration variable */
112 .nCtrimFractNew = 0, /* RCOSC calibration variable */
113 .nRtrimNew = 0, /* RCOSC calibration variable */
114 .nRtrimCurr = 0, /* RCOSC calibration variable */
115 .nDeltaFreqNew = 0, /* RCOSC calibration variable */
116 .bRefine = false, /* RCOSC calibration variable */
117 .state = Power_ACTIVE, /* current transition state */
118 .xoscPending = false, /* is XOSC_HF activation in progress? */
119 .calLF = false, /* calibrate RCOSC_LF? */
120 .auxHwiState = 0, /* calibration AUX ISR state */
121 .busyCal = false, /* already busy calibrating */
122 .calStep = 0, /* current calibration step */
123 .firstLF = true, /* is this first LF calibration? */
124 .enablePolicy = false, /* default value is false */
125 .initialized = false, /* whether Power_init has been called */
126 .constraintCounts = { 0, 0, 0, 0, 0, 0, 0 },
127 .resourceCounts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
128 .resourceHandlers = {
129 configureRFCoreClocks,
130 configureXOSCHF,
131 nopResourceHandler
132 }, /* special resource handler functions */
133 .policyFxn = 0 /* power policyFxn */
134 };
135
136 /* resource database */
137 const PowerCC26XX_ResourceRecord resourceDB[PowerCC26X2_NUMRESOURCES] = {
138 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER0}, /* PERIPH_GPT0 */
139 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER1}, /* PERIPH_GPT1 */
140 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER2}, /* PERIPH_GPT2 */
141 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER3}, /* PERIPH_GPT3 */
142 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_SSI0}, /* PERIPH_SSI0 */
143 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_SSI1}, /* PERIPH_SSI1 */
144 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_UART0}, /* PERIPH_UART0 */
145 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_I2C0}, /* PERIPH_I2C0 */
146 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TRNG}, /* PERIPH_TRNG */
147 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_GPIO}, /* PERIPH_GPIO */
148 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_UDMA}, /* PERIPH_UDMA */
149 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_CRYPTO}, /* PERIPH_CRYPTO */
150 {PowerCC26XX_PERIPH | PowerCC26XX_PERIPH_UDMA, PRCM_PERIPH_I2S}, /* PERIPH_I2S */
151 {PowerCC26XX_SPECIAL | PowerCC26XX_DOMAIN_RFCORE, 0}, /* PERIPH_RFCORE */
152 {PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 1}, /* XOSC_HF */
153 {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_PERIPH}, /* DOMAIN_PERIPH */
154 {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_SERIAL}, /* DOMAIN_SERIAL */
155 {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_RFCORE}, /* DOMAIN_RFCORE */
156 {PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 2}, /* DOMAIN_SYSBUS */
157 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_PKA}, /* PERIPH_PKA */
158 {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_UART1}, /* PERIPH_UART1 */
159 };
160
161 /* Defines */
162 #define TCXO_RAMP_DELAY 10
163 #define CC26X2_CLOCK_FREQUENCY 48000000
164
165 /* This is an approximate scaling factor previously used in test firmware. */
166 #define DELAY_SCALING_FACTOR 6000000
167
168 /* ****************** Power APIs ******************** */
169
170 /*
171 * ======== Power_disablePolicy ========
172 * Do not run the configured policy
173 */
Power_disablePolicy(void)174 bool Power_disablePolicy(void)
175 {
176 bool enablePolicy = PowerCC26X2_module.enablePolicy;
177 PowerCC26X2_module.enablePolicy = false;
178
179 return (enablePolicy);
180 }
181
182 /*
183 * ======== Power_enablePolicy ========
184 * Run the configured policy
185 */
Power_enablePolicy(void)186 void Power_enablePolicy(void)
187 {
188 PowerCC26X2_module.enablePolicy = true;
189 }
190
191 /*
192 * ======== Power_getConstraintMask ========
193 * Get a bitmask indicating the constraints that have been registered with
194 * Power.
195 */
Power_getConstraintMask(void)196 uint_fast32_t Power_getConstraintMask(void)
197 {
198 return (PowerCC26X2_module.constraintMask);
199 }
200
201 /*
202 * ======== Power_getDependencyCount ========
203 * Get the count of dependencies that are currently declared upon a resource.
204 */
Power_getDependencyCount(uint_fast16_t resourceId)205 int_fast16_t Power_getDependencyCount(uint_fast16_t resourceId)
206 {
207 DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES);
208
209 return ((int_fast16_t)PowerCC26X2_module.resourceCounts[resourceId]);
210 }
211
212 /*
213 * ======== Power_getTransitionLatency ========
214 * Get the transition latency for a sleep state. The latency is reported
215 * in units of microseconds.
216 */
Power_getTransitionLatency(uint_fast16_t sleepState,uint_fast16_t type)217 uint_fast32_t Power_getTransitionLatency(uint_fast16_t sleepState,
218 uint_fast16_t type)
219 {
220 uint32_t latency = 0;
221
222 if (type == Power_RESUME) {
223 if (sleepState == PowerCC26XX_STANDBY) {
224 latency = PowerCC26X2_RESUMETIMESTANDBY;
225 }
226 }
227 else {
228 if (sleepState == PowerCC26XX_STANDBY) {
229 latency = PowerCC26X2_TOTALTIMESTANDBY;
230 }
231 }
232
233 return (latency);
234 }
235
236 /*
237 * ======== Power_getTransitionState ========
238 * Get the current sleep transition state.
239 */
Power_getTransitionState(void)240 uint_fast16_t Power_getTransitionState(void)
241 {
242 return (PowerCC26X2_module.state);
243 }
244
245 /*
246 * ======== Power_idleFunc ========
247 * Function needs to be plugged into the idle loop.
248 * It calls the configured policy function if the
249 * 'enablePolicy' flag is set.
250 */
Power_idleFunc()251 void Power_idleFunc()
252 {
253 if (PowerCC26X2_module.enablePolicy) {
254 if (PowerCC26X2_module.policyFxn != NULL) {
255 (*(PowerCC26X2_module.policyFxn))();
256 }
257 }
258 }
259
260 /*
261 * ======== Power_init ========
262 */
Power_init()263 int_fast16_t Power_init()
264 {
265 ClockP_Params clockParams;
266 uint32_t ccfgLfClkSrc;
267
268 /* CC26X2 PG1.0 trap. If we are running on PG1.0, spin forever.
269 * This hardware revision is no longer supported. This trap is
270 * provided to aid in automatically identifying PG1.0 devices
271 * in circulation and will be removed later in the year.
272 */
273 if (!((HWREG(FCFG1_BASE + FCFG1_O_TFW_FT) % 10000) >= 683)) {
274 while (1);
275 }
276
277 /* if this function has already been called, just return */
278 if (PowerCC26X2_module.initialized) {
279 return (Power_SOK);
280 }
281
282 /* set module state field 'initialized' to true */
283 PowerCC26X2_module.initialized = true;
284
285 /* set the module state enablePolicy field */
286 PowerCC26X2_module.enablePolicy = PowerCC26X2_config.enablePolicy;
287
288 /* copy the Power policy function to module state */
289 PowerCC26X2_module.policyFxn = PowerCC26X2_config.policyFxn;
290
291 /* Check if TCXO is selected in CCFG and in addition configured to be
292 * enabled by the function pointed to by PowerCC26X2_config.enableTCXOFxn
293 */
294 if ((CCFGRead_XOSC_FREQ() == CCFGREAD_XOSC_FREQ_TCXO) &&
295 (PowerCC26X2_config.enableTCXOFxn != NULL)) {
296 /* Construct the Clock object for TCXO startup time.
297 * Set timeout to TCXO startup time as specified in CCFG.
298 */
299 ClockP_construct(&PowerCC26X2_module.tcxoEnableClock,
300 (ClockP_Fxn)&switchToTCXO,
301 (CCFGRead_TCXO_MAX_START()*100)/ClockP_getSystemTickPeriod(),
302 NULL);
303
304 HWREG(AUX_DDI0_OSC_BASE + DDI_O_CLR + DDI_0_OSC_O_CTL0) = DDI_0_OSC_CTL0_XTAL_IS_24M;
305 }
306
307 /* construct the Clock object for scheduling of wakeups */
308 /* initiated and started by the power policy */
309 ClockP_Params_init(&clockParams);
310 clockParams.period = 0;
311 clockParams.startFlag = false;
312 clockParams.arg = 0;
313 ClockP_construct(&PowerCC26X2_module.clockObj,
314 &emptyClockFunc,
315 0,
316 &clockParams);
317
318 /*
319 * If RCOSC calibration is enabled, construct a Clock object for
320 * delays. Set timeout to 8 Clock tick periods to get
321 * ceil(8x10us/30.5us/SCLK_LF_period)*30.5us/SCLK_LF_period = ~90us.
322 * The total time we need to wait for AUX_SYSIF_TDCREFCLKCTL_ACK
323 * is about 105us and the ClockP_start() call needs about 21us.
324 * All together, that makes ~111us. A decent approximation of the
325 * ideal wait duration.
326 * In practice, the COMPARE_MARGIN that is currently still in
327 * the kernel Timer.c implementation may make it take longer
328 * than 90us to time out.
329 */
330 ClockP_Params_init(&clockParams);
331 clockParams.period = 0;
332 clockParams.startFlag = false;
333 clockParams.arg = 0;
334 ClockP_construct(&PowerCC26X2_module.calibrationClock,
335 &PowerCC26X2_RCOSC_clockFunc,
336 8,
337 &clockParams);
338
339 HwiP_construct(&PowerCC26X2_module.oscHwi,
340 INT_OSC_COMB,
341 oscillatorISR, NULL);
342 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = 0;
343
344 /* construct the TDC hwi */
345 HwiP_construct(&PowerCC26X2_module.tdcHwi,
346 INT_AUX_COMB,
347 PowerCC26X2_auxISR, NULL);
348
349 DRIVERLIB_ASSERT_CURR_RELEASE();
350
351 /* read the LF clock source from CCFG */
352 ccfgLfClkSrc = CCFGRead_SCLK_LF_OPTION();
353
354 /* check if should calibrate RCOSC_LF */
355 if (PowerCC26X2_config.calibrateRCOSC_LF) {
356 /* verify RCOSC_LF is the LF clock source */
357 if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) {
358 PowerCC26X2_module.calLF = true;
359 }
360 }
361
362 /*
363 * if LF source is RCOSC_LF or XOSC_LF: assert DISALLOW_STANDBY constraint
364 * and start a timeout to check for activation
365 */
366 if ((ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) ||
367 (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_LF)) {
368
369 /* Turn on oscillator interrupt for SCLK_LF switching */
370 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_LFSRCDONEIM_M;
371
372 /* disallow STANDBY pending LF clock quailifier disabling */
373 Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
374 }
375 else if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_EXTERNAL_LF) {
376 /*
377 * else, if the LF clock source is external, can disable clock qualifiers
378 * now; no need to assert DISALLOW_STANDBY or start the Clock object
379 */
380
381 /* yes, disable the LF clock qualifiers */
382 DDI16BitfieldWrite(
383 AUX_DDI0_OSC_BASE,
384 DDI_0_OSC_O_CTL0,
385 DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M|
386 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M,
387 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S,
388 0x3);
389
390 /* enable clock loss detection */
391 OSCClockLossEventEnable();
392 }
393 else if(ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_HF_DLF) {
394 /* else, user has requested LF to be derived from XOSC_HF */
395
396 /* Turn on oscillator interrupt for SCLK_LF switching.
397 * When using HPOSC, the LF clock will already have switched
398 * and the interrupt will fire once interrupts are enabled
399 * again when the OS starts.
400 * When using a regular HF crystal, it may take a little
401 * time for the crystal to start up
402 */
403 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_LFSRCDONEIM_M;
404
405 /* disallow standby since we cannot go into standby with
406 * an HF derived LF clock
407 */
408 Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
409 }
410
411 /* if VIMS RAM is configured as GPRAM: set retention constraint */
412 if (!CCFGRead_DIS_GPRAM()) {
413 Power_setConstraint(PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY);
414 }
415
416 return (Power_SOK);
417 }
418
419 /*
420 * ======== Power_registerNotify ========
421 * Register a function to be called on a specific power event.
422 *
423 */
Power_registerNotify(Power_NotifyObj * pNotifyObj,uint_fast16_t eventTypes,Power_NotifyFxn notifyFxn,uintptr_t clientArg)424 int_fast16_t Power_registerNotify(Power_NotifyObj * pNotifyObj,
425 uint_fast16_t eventTypes, Power_NotifyFxn notifyFxn, uintptr_t clientArg)
426 {
427 int_fast16_t status = Power_SOK;
428
429 /* check for NULL pointers */
430 if ((pNotifyObj == NULL) || (notifyFxn == NULL)) {
431 status = Power_EINVALIDPOINTER;
432 }
433
434 else {
435 /* fill in notify object elements */
436 pNotifyObj->eventTypes = eventTypes;
437 pNotifyObj->notifyFxn = notifyFxn;
438 pNotifyObj->clientArg = clientArg;
439
440 /* place notify object on event notification queue */
441 List_put(&PowerCC26X2_module.notifyList, (List_Elem*)pNotifyObj);
442 }
443
444 return (status);
445 }
446
447 /*
448 * ======== Power_releaseConstraint ========
449 * Release a previously declared constraint.
450 */
Power_releaseConstraint(uint_fast16_t constraintId)451 int_fast16_t Power_releaseConstraint(uint_fast16_t constraintId)
452 {
453 unsigned int key;
454 uint8_t count;
455
456 /* assert constraintId is valid */
457 DebugP_assert(constraintId < PowerCC26X2_NUMCONSTRAINTS);
458
459 key = HwiP_disable();
460
461 /* get the count of the constraint */
462 count = PowerCC26X2_module.constraintCounts[constraintId];
463
464 DebugP_assert(count != 0);
465
466 count--;
467
468 /* save the updated count */
469 PowerCC26X2_module.constraintCounts[constraintId] = count;
470
471 if (count == 0) {
472 PowerCC26X2_module.constraintMask &= ~(1 << constraintId);
473 }
474
475 HwiP_restore(key);
476
477 return (Power_SOK);
478 }
479
480 /*
481 * ======== Power_releaseDependency ========
482 * Release a previously declared dependency.
483 */
Power_releaseDependency(uint_fast16_t resourceId)484 int_fast16_t Power_releaseDependency(uint_fast16_t resourceId)
485 {
486 uint8_t parent;
487 uint8_t count;
488 uint32_t id;
489 unsigned int key;
490
491 /* assert resourceId is valid */
492 DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES);
493
494 /* disable interrupts */
495 key = HwiP_disable();
496
497 /* read and decrement the reference count */
498 count = PowerCC26X2_module.resourceCounts[resourceId];
499
500 DebugP_assert(count != 0);
501
502 count--;
503
504 /* save the reference count */
505 PowerCC26X2_module.resourceCounts[resourceId] = count;
506
507 /* if this was the last dependency being released.., */
508 if (count == 0) {
509 /* deactivate this resource ... */
510 id = resourceDB[resourceId].driverlibID;
511
512 /* is resource a peripheral?... */
513 if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) {
514 PRCMPeripheralRunDisable(id);
515 PRCMPeripheralSleepDisable(id);
516 PRCMPeripheralDeepSleepDisable(id);
517 PRCMLoadSet();
518 while (!PRCMLoadGet()) {
519 ;
520 }
521 }
522 /* else, does resource require a special handler?... */
523 else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) {
524 /* call the special handler */
525 PowerCC26X2_module.resourceHandlers[id](PowerCC26XX_DISABLE);
526 }
527
528 /* else resource is a power domain */
529 else {
530 PRCMPowerDomainOff(id);
531 while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_OFF) {
532 ;
533 }
534 }
535
536 /* propagate release up the dependency tree ... */
537
538 /* check for a first parent */
539 parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK;
540
541 /* if 1st parent, make recursive call to release that dependency */
542 if (parent < PowerCC26X2_NUMRESOURCES) {
543 Power_releaseDependency(parent);
544 }
545 }
546
547 /* re-enable interrupts */
548 HwiP_restore(key);
549
550 return (Power_SOK);
551 }
552
553 /*
554 * ======== Power_setConstraint ========
555 * Declare an operational constraint.
556 */
Power_setConstraint(uint_fast16_t constraintId)557 int_fast16_t Power_setConstraint(uint_fast16_t constraintId)
558 {
559 unsigned int key;
560
561 /* assert constraint id is valid */
562 DebugP_assert(constraintId < PowerCC26X2_NUMCONSTRAINTS);
563
564 /* disable interrupts */
565 key = HwiP_disable();
566
567 /* set the specified constraint in the constraintMask */
568 PowerCC26X2_module.constraintMask |= 1 << constraintId;
569
570 /* increment the specified constraint count */
571 PowerCC26X2_module.constraintCounts[constraintId]++;
572
573 /* re-enable interrupts */
574 HwiP_restore(key);
575
576 return (Power_SOK);
577 }
578
579 /*
580 * ======== Power_setDependency ========
581 * Declare a dependency upon a resource.
582 */
Power_setDependency(uint_fast16_t resourceId)583 int_fast16_t Power_setDependency(uint_fast16_t resourceId)
584 {
585 uint8_t parent;
586 uint8_t count;
587 uint32_t id;
588 unsigned int key;
589
590 /* assert resourceId is valid */
591 DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES);
592
593 /* disable interrupts */
594 key = HwiP_disable();
595
596 /* read and increment reference count */
597 count = PowerCC26X2_module.resourceCounts[resourceId]++;
598
599 /* if resource was NOT activated previously ... */
600 if (count == 0) {
601 /* propagate set up the dependency tree ... */
602
603 /* check for a first parent */
604 parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK;
605
606 /* if first parent, make recursive call to set that dependency */
607 if (parent < PowerCC26X2_NUMRESOURCES) {
608 Power_setDependency(parent);
609 }
610
611 /* now activate this resource ... */
612 id = resourceDB[resourceId].driverlibID;
613
614 /* is resource a peripheral?... */
615 if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) {
616 PRCMPeripheralRunEnable(id);
617 PRCMPeripheralSleepEnable(id);
618 PRCMPeripheralDeepSleepEnable(id);
619 PRCMLoadSet();
620 while (!PRCMLoadGet()) {
621 ;
622 }
623 }
624 /* else, does resource require a special handler?... */
625 else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) {
626 /* call the special handler */
627 PowerCC26X2_module.resourceHandlers[id](PowerCC26XX_ENABLE);
628 }
629 /* else resource is a power domain */
630 else {
631 PRCMPowerDomainOn(id);
632 while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_ON) {
633 ;
634 }
635 }
636 }
637
638 /* re-enable interrupts */
639 HwiP_restore(key);
640
641 return (Power_SOK);
642 }
643
644 /*
645 * ======== Power_setPolicy ========
646 * Set the Power policy function
647 */
Power_setPolicy(Power_PolicyFxn policy)648 void Power_setPolicy(Power_PolicyFxn policy)
649 {
650 PowerCC26X2_module.policyFxn = policy;
651 }
652
653 /*
654 * ======== Power_shutdown ========
655 */
Power_shutdown(uint_fast16_t shutdownState,uint_fast32_t shutdownTime)656 int_fast16_t Power_shutdown(uint_fast16_t shutdownState,
657 uint_fast32_t shutdownTime)
658 {
659 int_fast16_t status = Power_EFAIL;
660 unsigned int constraints;
661 unsigned int hwiKey;
662
663 /* disable interrupts */
664 hwiKey = HwiP_disable();
665
666 /* check if there is a constraint to prohibit shutdown */
667 constraints = Power_getConstraintMask();
668 if (constraints & (1 << PowerCC26XX_DISALLOW_SHUTDOWN)) {
669 status = Power_ECHANGE_NOT_ALLOWED;
670 }
671
672 /* OK to shutdown ... */
673 else if (PowerCC26X2_module.state == Power_ACTIVE) {
674 /* set new transition state to entering shutdown */
675 PowerCC26X2_module.state = Power_ENTERING_SHUTDOWN;
676
677 /* signal all clients registered for pre-shutdown notification */
678 status = notify(PowerCC26XX_ENTERING_SHUTDOWN);
679
680 /* check for any error */
681 if (status != Power_SOK) {
682 PowerCC26X2_module.state = Power_ACTIVE;
683 HwiP_restore(hwiKey);
684 return (status);
685 }
686
687 /* Ensure the JTAG domain is turned off
688 * otherwise MCU domain can't be turned off.
689 */
690 HWREG(AON_PMCTL_BASE + AON_PMCTL_O_JTAGCFG) = 0;
691
692 SysCtrlAonSync();
693
694 /* now proceed with shutdown sequence ... */
695 SysCtrlShutdownWithAbort();
696 }
697 else {
698 status = Power_EBUSY;
699 }
700
701 /* NOTE: if shutdown succeeded, should never get here */
702
703 /* return failure status */
704 PowerCC26X2_module.state = Power_ACTIVE;
705
706 /* re-enable interrupts */
707 HwiP_restore(hwiKey);
708
709 /* if get here, failed to shutdown, return error code */
710 return (status);
711 }
712
713 /*
714 * ======== Power_sleep ========
715 */
Power_sleep(uint_fast16_t sleepState)716 int_fast16_t Power_sleep(uint_fast16_t sleepState)
717 {
718 int_fast16_t status = Power_SOK;
719 int_fast16_t notifyStatus = Power_SOK;
720 int_fast16_t lateNotifyStatus = Power_SOK;
721 unsigned int xosc_hf_active = false;
722 uint_fast16_t postEventLate;
723 uint32_t poweredDomains = 0;
724 uint_fast16_t preEvent;
725 uint_fast16_t postEvent;
726 unsigned int constraints;
727 bool retainCache = false;
728 uint32_t modeVIMS;
729
730 /* first validate the sleep code */
731 if (sleepState != PowerCC26XX_STANDBY) {
732 status = Power_EINVALIDINPUT;
733 }
734
735 else {
736
737 /* check to make sure Power is not busy with another transition */
738 if (PowerCC26X2_module.state == Power_ACTIVE) {
739 /* set transition state to entering sleep */
740 PowerCC26X2_module.state = Power_ENTERING_SLEEP;
741 }
742 else {
743 status = Power_EBUSY;
744 }
745
746 if (status == Power_SOK) {
747
748 /* setup sleep vars */
749 preEvent = PowerCC26XX_ENTERING_STANDBY;
750 postEvent = PowerCC26XX_AWAKE_STANDBY;
751 postEventLate = PowerCC26XX_AWAKE_STANDBY_LATE;
752
753 /* disable scheduling */
754 PowerCC26XX_schedulerDisable();
755
756 /* signal all clients registered for pre-sleep notification */
757 status = notify(preEvent);
758
759 /* check for any error */
760 if (status != Power_SOK) {
761 PowerCC26X2_module.state = Power_ACTIVE;
762 PowerCC26XX_schedulerRestore();
763 return (status);
764 }
765
766 /* 1. Query and save domain states before powering them off */
767 if (Power_getDependencyCount(PowerCC26XX_DOMAIN_RFCORE)) {
768 poweredDomains |= PRCM_DOMAIN_RFCORE;
769 }
770 if (Power_getDependencyCount(PowerCC26XX_DOMAIN_SERIAL)){
771 poweredDomains |= PRCM_DOMAIN_SERIAL;
772 }
773 if (Power_getDependencyCount(PowerCC26XX_DOMAIN_PERIPH)) {
774 poweredDomains |= PRCM_DOMAIN_PERIPH;
775 }
776
777 /* 2. If XOSC_HF is active or we are waiting to switch
778 * to it, force it off. Otherwise, the XOSC_HF may be
779 * automatically turned on by the hardware without
780 * a call to configureXOSCHF(PowerCC26XX_ENABLE)
781 * This is not necessarily a problem. However exactly
782 * what the cutoff point is where the hardware considers
783 * the XOSC_HF "on" without having switched to is not
784 * considered by this driver.
785 */
786 if (OSCClockSourceGet(OSC_SRC_CLK_HF) == OSC_XOSC_HF ||
787 PowerCC26X2_module.xoscPending == true) {
788 xosc_hf_active = true;
789 configureXOSCHF(PowerCC26XX_DISABLE);
790 }
791
792 /* query constraints to determine if cache should be retained */
793 constraints = Power_getConstraintMask();
794 if (constraints & (1 << PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY)) {
795 retainCache = true;
796 }
797 else {
798 retainCache = false;
799
800 // Get the current VIMS mode
801 do {
802 modeVIMS = VIMSModeGet(VIMS_BASE);
803 } while (modeVIMS == VIMS_MODE_CHANGING);
804 }
805
806 /* 3.
807 * - Freeze the IOs on the boundary between MCU and AON
808 * - Make sure AON writes take effect
809 * - Request power off of every PD in the MCU voltage domain
810 * - Ensure that no clocks are forced on in Crypto, DMA and I2S
811 * - Gate running deep sleep clocks for Crypto, DMA and I2S
812 * - Load the new clock settings
813 * - Configure the VIMS power domain mode to power up flash
814 * again after coming out of standby.
815 * - Request uLDO during standby
816 * - Use recharge comparator
817 * - Ensure all writes have taken effect
818 * - Ensure UDMA, Crypto and I2C clocks are turned off
819 * - Ensure all non-CPU power domains are turned off
820 * - Turn off cache retention if requested
821 * - Invoke deep sleep to go to standby
822 */
823 SysCtrlStandby(retainCache,
824 VIMS_ON_CPU_ON_MODE,
825 SYSCTRL_PREFERRED_RECHARGE_MODE);
826
827 /* 4. If didn't retain VIMS in standby, re-enable retention now */
828 if (retainCache == false) {
829
830 /* 5.1 If previously in a cache mode, restore the mode now */
831 if (modeVIMS == VIMS_MODE_ENABLED) {
832 VIMSModeSet(VIMS_BASE, modeVIMS);
833 }
834
835 /* 5.2 Re-enable retention */
836 PRCMCacheRetentionEnable();
837 }
838
839 /* 6. Start re-powering power domains */
840 PRCMPowerDomainOn(poweredDomains);
841
842 /* 7. Restore deep sleep clocks of Crypto and DMA */
843 if (Power_getDependencyCount(PowerCC26XX_PERIPH_CRYPTO)) {
844 PRCMPeripheralDeepSleepEnable(
845 resourceDB[PowerCC26XX_PERIPH_CRYPTO].driverlibID);
846 }
847 if (Power_getDependencyCount(PowerCC26XX_PERIPH_UDMA)) {
848 PRCMPeripheralDeepSleepEnable(
849 resourceDB[PowerCC26XX_PERIPH_UDMA].driverlibID);
850 }
851
852 /* 8. Make sure clock settings take effect */
853 PRCMLoadSet();
854
855 /* 9. Release request for uLDO */
856 PRCMMcuUldoConfigure(false);
857
858 /* 10. Set transition state to EXITING_SLEEP */
859 PowerCC26X2_module.state = Power_EXITING_SLEEP;
860
861 /* 11. Wait until all power domains are back on */
862 while (PRCMPowerDomainStatus(poweredDomains) != PRCM_DOMAIN_POWER_ON);
863
864 /* 12. Wait for the RTC shadow values to be updated so that
865 * the early notification callbacks can read out valid RTC values.
866 * This can likely be removed as the 2MHz MF clock will have ticked by now.
867 */
868 SysCtrlAonSync();
869
870 /*
871 * 13. Signal clients registered for early post-sleep notification;
872 * this should be used to initialize any timing critical or IO
873 * dependent hardware
874 */
875 notifyStatus = notify(postEvent);
876
877 /* 14. Disable IO freeze and ensure RTC shadow value is updated */
878 AONIOCFreezeDisable();
879 SysCtrlAonSync();
880
881 /* 15. If XOSC_HF was forced off above, initiate switch back */
882 if (xosc_hf_active == true) {
883 configureXOSCHF(PowerCC26XX_ENABLE);
884 }
885
886 /* 16. Re-enable interrupts */
887 /* For Zephyr, post suspend hooks need to run with interrupts
888 * disabled after Power_sleep returns. So we need to leave
889 * interrupts disabled.
890 */
891 /* CPUcpsie(); */
892
893 /*
894 * 17. Signal all clients registered for late post-sleep
895 * notification
896 */
897 lateNotifyStatus = notify(postEventLate);
898
899 /*
900 * 18. Now clear the transition state before re-enabling
901 * scheduler
902 */
903 PowerCC26X2_module.state = Power_ACTIVE;
904
905 /* 19. Re-enable scheduling */
906 PowerCC26XX_schedulerRestore();
907
908 /* if there was a notification error, set return status */
909 if ((notifyStatus != Power_SOK) ||
910 (lateNotifyStatus != Power_SOK)) {
911 status = Power_EFAIL;
912 }
913 }
914 }
915
916 return (status);
917 }
918
919 /*
920 * ======== Power_unregisterNotify ========
921 * Unregister for a power notification.
922 *
923 */
Power_unregisterNotify(Power_NotifyObj * pNotifyObj)924 void Power_unregisterNotify(Power_NotifyObj * pNotifyObj)
925 {
926 unsigned int key;
927
928 /* remove notify object from its event queue */
929 key = HwiP_disable();
930
931 /* remove notify object from its event queue */
932 List_remove(&PowerCC26X2_module.notifyList, (List_Elem *)pNotifyObj);
933
934 HwiP_restore(key);
935 }
936
937 /* ****************** CC26XX specific APIs ******************** */
938
939 /*
940 * ======== PowerCC26X2_enableHposcRtcCompensation ========
941 * This function enabled temperature based compensation of the RTC when
942 * SCLK_LF is derived from HPOSC.
943 */
PowerCC26X2_enableHposcRtcCompensation(void)944 void PowerCC26X2_enableHposcRtcCompensation(void) {
945 /* If we are using HPOSC and SCLK_LF is derived from it, we need to
946 * compensate the RTC to account for HPOSC frequency drift over temperature.
947 */
948 if (OSC_IsHPOSCEnabledWithHfDerivedLfClock()) {
949 Temperature_init();
950
951 int16_t currentTemperature = Temperature_getTemperature();
952
953 OSC_HPOSCInitializeFrequencyOffsetParameters();
954
955 /* The compensation fxn will register itself with updated thresholds
956 * based on the current temperature each time it is invoked. If we
957 * call it from the init function, it will register itself for the
958 * first time and handle initial RTC compensation.
959 */
960 hposcRtcCompensateFxn(currentTemperature,
961 currentTemperature + PowerCC26X2_HPOSC_RTC_COMPENSATION_DELTA,
962 (uintptr_t)NULL,
963 &PowerCC26X2_module.hposcRtcCompNotifyObj);
964 }
965 }
966
967
968 /*
969 * ======== PowerCC26XX_calibrate ========
970 * Plug this function into the PowerCC26X2_Config structure
971 * if calibration is needed.
972 */
PowerCC26XX_calibrate(unsigned int arg)973 bool PowerCC26XX_calibrate(unsigned int arg)
974 {
975 bool retVal = false;
976
977 switch (arg) {
978 case PowerCC26X2_INITIATE_CALIBRATE:
979 retVal = PowerCC26X2_initiateCalibration();
980 break;
981
982 case PowerCC26X2_DO_CALIBRATE:
983 PowerCC26X2_calibrate();
984 break;
985 default:
986 while (1);
987 }
988
989 return (retVal);
990 }
991
992 /*
993 * ======== PowerCC26XX_doWFI ========
994 */
PowerCC26XX_doWFI(void)995 void PowerCC26XX_doWFI(void)
996 {
997 __asm(" wfi");
998 }
999
1000 /*
1001 * ======== PowerCC26X2_getClockHandle ========
1002 */
PowerCC26XX_getClockHandle()1003 ClockP_Handle PowerCC26XX_getClockHandle()
1004 {
1005 return ((ClockP_Handle)&PowerCC26X2_module.clockObj);
1006 }
1007
1008 /*
1009 * ======== PowerCC26XX_noCalibrate ========
1010 * Plug this function into the PowerCC26X2 config structure if calibration
1011 * is not needed.
1012 */
PowerCC26XX_noCalibrate(unsigned int arg)1013 bool PowerCC26XX_noCalibrate(unsigned int arg)
1014 {
1015 return (0);
1016 }
1017
1018 /*
1019 * ======== PowerCC26XX_getXoscStartupTime ========
1020 * Get the estimated crystal oscillator startup time
1021 */
PowerCC26XX_getXoscStartupTime(uint32_t timeUntilWakeupInMs)1022 uint32_t PowerCC26XX_getXoscStartupTime(uint32_t timeUntilWakeupInMs)
1023 {
1024 return (OSCHF_GetStartupTime(timeUntilWakeupInMs));
1025 }
1026
1027 /*
1028 * ======== PowerCC26X2_injectCalibration ========
1029 * Explicitly trigger RCOSC calibration
1030 */
PowerCC26XX_injectCalibration(void)1031 bool PowerCC26XX_injectCalibration(void)
1032 {
1033 if ((*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE)) {
1034 /* here if AUX SMPH was available, start calibration now ... */
1035 (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE);
1036 return (true);
1037 }
1038
1039 return (false);
1040 }
1041
1042 /*
1043 * ======== PowerCC26XX_isStableXOSC_HF ========
1044 * Check if XOSC_HF has stabilized.
1045 */
PowerCC26XX_isStableXOSC_HF(void)1046 bool PowerCC26XX_isStableXOSC_HF(void)
1047 {
1048 bool ready = true;
1049 unsigned int key;
1050
1051 key = HwiP_disable();
1052
1053 /* only query if HF source is ready if there is a pending change */
1054 if (PowerCC26X2_module.xoscPending) {
1055 ready = OSCHfSourceReady();
1056 }
1057
1058 HwiP_restore(key);
1059
1060 return (ready);
1061 }
1062
1063 /*
1064 * ======== PowerCC26XX_switchXOSC_HF ========
1065 * Switch to enable XOSC_HF.
1066 * May only be called when using the PowerCC26XX_SWITCH_XOSC_HF_MANUALLY
1067 * constraint.
1068 * May only be called after ensuring the XOSC_HF is stable by calling
1069 * PowerCC26XX_isStableXOSC_HF().
1070 */
PowerCC26XX_switchXOSC_HF(void)1071 void PowerCC26XX_switchXOSC_HF(void)
1072 {
1073 bool readyToCal;
1074 unsigned int key;
1075
1076 key = HwiP_disable();
1077
1078 /* Since PowerCC26X2_isStableXOSC_HF() should have been called before this
1079 * function, we can just switch without handling the case when the XOSC_HF
1080 * is not ready or PowerCC26X2_module.xoscPending is not true.
1081 */
1082 OSCHF_AttemptToSwitchToXosc();
1083
1084 /* Since configureXOSCHF() was called prior to this function to turn
1085 * on the XOSC_HF, PowerCC26X2_module.xoscPending will be true and
1086 * we can safely set it to false.
1087 */
1088 PowerCC26X2_module.xoscPending = false;
1089
1090 /* Allow going into IDLE again since we sucessfully switched
1091 * to XOSC_HF
1092 */
1093 Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE);
1094
1095 HwiP_restore(key);
1096
1097 /* initiate RCOSC calibration */
1098 readyToCal = (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE);
1099
1100 /* now notify clients that were waiting for a switch notification */
1101 notify(PowerCC26XX_XOSC_HF_SWITCHED);
1102
1103 /* if ready to start first cal measurment, do it now */
1104 if (readyToCal == true) {
1105 (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE);
1106 }
1107 }
1108
1109 /* * * * * * * * * * * internal and support functions * * * * * * * * * * */
1110
1111 /*
1112 * ======== hposcRtcCompensateFxn ========
1113 */
hposcRtcCompensateFxn(int16_t currentTemperature,int16_t thresholdTemperature,uintptr_t clientArg,Temperature_NotifyObj * notifyObject)1114 void hposcRtcCompensateFxn(int16_t currentTemperature,
1115 int16_t thresholdTemperature,
1116 uintptr_t clientArg,
1117 Temperature_NotifyObj *notifyObject) {
1118 int_fast16_t status;
1119 int32_t relFreqOffset;
1120
1121 relFreqOffset = OSC_HPOSCRelativeFrequencyOffsetGet(currentTemperature);
1122
1123 OSC_HPOSCRtcCompensate(relFreqOffset);
1124
1125 /* Register the notification again with updated thresholds */
1126 status = Temperature_registerNotifyRange(notifyObject,
1127 currentTemperature + PowerCC26X2_HPOSC_RTC_COMPENSATION_DELTA,
1128 currentTemperature - PowerCC26X2_HPOSC_RTC_COMPENSATION_DELTA,
1129 hposcRtcCompensateFxn,
1130 (uintptr_t)NULL);
1131
1132 if (status != Temperature_STATUS_SUCCESS) {
1133 while(1);
1134 }
1135 }
1136
1137 /*
1138 * ======== oscillatorISR ========
1139 */
oscillatorISR(uintptr_t arg)1140 static void oscillatorISR(uintptr_t arg)
1141 {
1142 uint32_t rawStatus = HWREG(PRCM_BASE + PRCM_O_OSCRIS);
1143 uint32_t intStatusMask = HWREG(PRCM_BASE + PRCM_O_OSCIMSC);
1144
1145 /* Turn off mask for all flags we will handle */
1146 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = intStatusMask & ~rawStatus;
1147
1148 /* XOSC_LF or RCOSC_LF qualified */
1149 if (rawStatus & PRCM_OSCRIS_LFSRCDONERIS_M & intStatusMask) {
1150 disableLFClockQualifiers();
1151 }
1152
1153 /* XOSC_HF ready to switch to */
1154 if (rawStatus & PRCM_OSCIMSC_HFSRCPENDIM_M & intStatusMask) {
1155 switchXOSCHF();
1156 }
1157
1158 /* Clear flags we will handle. Does not really work as expected as
1159 * the flags seem to level-detect and not edge-detect. Until the
1160 * underlying trigger is taken care of, the flag will not deassert
1161 * even when cleared.
1162 * We're clearing at the end in order to prevent the flag from
1163 * immediately asserting again if the underlying trigger was
1164 * not handled yet.
1165 * SCLK_LF switched can never be cleared after triggering
1166 * only masked out. XOSC_HF ready to switch can be cleared
1167 * after switching to XOSC_HF.
1168 */
1169 HWREG(PRCM_BASE + PRCM_O_OSCICR) = intStatusMask & rawStatus;
1170 }
1171
1172 /*
1173 * ======== emptyClockFunc ========
1174 * Clock function used by power policy to schedule early wakeups.
1175 */
emptyClockFunc(uintptr_t arg)1176 static void emptyClockFunc(uintptr_t arg)
1177 {
1178 }
1179
1180 /*
1181 * ======== disableLFClockQualifiers ========
1182 * Clock function used for delayed disable of LF clock qualifiers.
1183 */
disableLFClockQualifiers(void)1184 static void disableLFClockQualifiers(void)
1185 {
1186 uint32_t sourceLF;
1187
1188 /* query LF clock source */
1189 sourceLF = OSCClockSourceGet(OSC_SRC_CLK_LF);
1190
1191 /* is LF source either RCOSC_LF or XOSC_LF yet? */
1192 if ((sourceLF == OSC_RCOSC_LF) || (sourceLF == OSC_XOSC_LF)) {
1193
1194 /* yes, disable the LF clock qualifiers */
1195 DDI16BitfieldWrite(
1196 AUX_DDI0_OSC_BASE,
1197 DDI_0_OSC_O_CTL0,
1198 DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M|
1199 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M,
1200 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S,
1201 0x3
1202 );
1203
1204 /* enable clock loss detection */
1205 OSCClockLossEventEnable();
1206
1207 /* now finish by releasing the standby disallow constraint */
1208 Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
1209 }
1210 else if(sourceLF == OSC_XOSC_HF) {
1211 /* yes, disable the LF clock qualifiers */
1212 DDI16BitfieldWrite(
1213 AUX_DDI0_OSC_BASE,
1214 DDI_0_OSC_O_CTL0,
1215 DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M|
1216 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M,
1217 DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S,
1218 0x3
1219 );
1220
1221 /* enable clock loss detection */
1222 OSCClockLossEventEnable();
1223
1224 /* do not allow standby since the LF clock is HF derived */
1225 }
1226
1227 }
1228
1229 /*
1230 * ======== nopResourceFunc ========
1231 * special resource handler
1232 */
nopResourceHandler(unsigned int action)1233 static unsigned int nopResourceHandler(unsigned int action)
1234 {
1235 return (0);
1236 }
1237
1238 /*
1239 * ======== delayUs ========
1240 * Polls for an approximate number of us. Not very accurate.
1241 * In this use case we only care about waiting 'at least X'
1242 * and a few extra microseconds will only allow additional
1243 * stabilisation time.
1244 */
delayUs(uint32_t us)1245 static void delayUs(uint32_t us) {
1246 CPUdelay((CC26X2_CLOCK_FREQUENCY / DELAY_SCALING_FACTOR) * us);
1247 }
1248
1249 /*
1250 * ======== notify ========
1251 * Send notifications to registered clients.
1252 * Note: Task scheduling is disabled when this function is called.
1253 */
notify(uint_fast16_t eventType)1254 static int_fast16_t notify(uint_fast16_t eventType)
1255 {
1256 int_fast16_t notifyStatus;
1257 Power_NotifyFxn notifyFxn;
1258 uintptr_t clientArg;
1259 List_Elem *elem;
1260
1261 /* if queue is empty, return immediately */
1262 if (!List_empty(&PowerCC26X2_module.notifyList)) {
1263 /* point to first client notify object */
1264 elem = List_head(&PowerCC26X2_module.notifyList);
1265
1266 /* walk the queue and notify each registered client of the event */
1267 do {
1268 if (((Power_NotifyObj *)elem)->eventTypes & eventType) {
1269 /* pull params from notify object */
1270 notifyFxn = ((Power_NotifyObj *)elem)->notifyFxn;
1271 clientArg = ((Power_NotifyObj *)elem)->clientArg;
1272
1273 /* call the client's notification function */
1274 notifyStatus = (int_fast16_t)(*(Power_NotifyFxn)notifyFxn)(
1275 eventType, 0, clientArg);
1276
1277 /* if client declared error stop all further notifications */
1278 if (notifyStatus != Power_NOTIFYDONE) {
1279 return (Power_EFAIL);
1280 }
1281 }
1282
1283 /* get next element in the notification queue */
1284 elem = List_next(elem);
1285
1286 } while (elem != NULL);
1287 }
1288
1289 return (Power_SOK);
1290 }
1291
1292 /*
1293 * ======== configureRFCoreClocks ========
1294 * Special dependency function for controlling RF core clocks.
1295 * This function does nothing, but is kept for legacy reasons.
1296 * All functionality has been integrated into the RF driver.
1297 */
configureRFCoreClocks(unsigned int action)1298 static unsigned int configureRFCoreClocks(unsigned int action)
1299 {
1300 return (0);
1301 }
1302
1303 /*
1304 * ======== switchXOSCHF ========
1305 * Switching to XOSC_HF when it has stabilized.
1306 */
switchXOSCHF(void)1307 static void switchXOSCHF(void)
1308 {
1309 bool readyToCal;
1310 unsigned int key;
1311
1312 key = HwiP_disable();
1313
1314 /* Switch to the XOSC_HF. Since this function is only called
1315 * after we get an interrupt signifying it is ready to switch,
1316 * it should always succeed.
1317 * If it does not succeed, try again. It is fine if we spin,
1318 * there is no sensible recovery mechanism from such an error.
1319 */
1320 while (!OSCHF_AttemptToSwitchToXosc());
1321
1322 /* The only time we should get here is when PowerCC26X2_module.xoscPending == true
1323 * holds.
1324 * Allow going into IDLE again since we sucessfully switched
1325 * to XOSC_HF
1326 */
1327 Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE);
1328
1329 PowerCC26X2_module.xoscPending = false;
1330
1331
1332 HwiP_restore(key);
1333
1334 /* initiate RCOSC calibration */
1335 readyToCal = (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE);
1336
1337 /* now notify clients that were waiting for a switch notification */
1338 notify(PowerCC26XX_XOSC_HF_SWITCHED);
1339
1340 /* if ready to start first cal measurment, do it now */
1341 if (readyToCal == true) {
1342 (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE);
1343 }
1344 }
1345
1346 /*
1347 * ======== configureXOSCHF ========
1348 */
configureXOSCHF(unsigned int action)1349 static unsigned int configureXOSCHF(unsigned int action)
1350 {
1351 /* By checking action == PowerCC26XX_ENABLE and PowerCC26X2_module.xoscPending
1352 * carefully, the function should be idempotent. Calling it with the same
1353 * action more than once will not have any effect until the hardware triggers
1354 * a software state change.
1355 */
1356 if (action == PowerCC26XX_ENABLE &&
1357 OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF &&
1358 PowerCC26X2_module.xoscPending == false) {
1359
1360 /* Check if TCXO is selected in CCFG and in addition configured to be enabled
1361 * by the function pointed to by PowerCC26X2_config.enableTCXOFxn.
1362 */
1363 if ((CCFGRead_XOSC_FREQ() == CCFGREAD_XOSC_FREQ_TCXO) &&
1364 (PowerCC26X2_config.enableTCXOFxn != NULL)) {
1365
1366 /* Enable clock qualification on 48MHz signal from TCXO */
1367 if (CCFGRead_TCXO_TYPE() == 0x1) {
1368 /* If the selected TCXO type is clipped-sine, also enable
1369 * internal common-mode bias
1370 */
1371 HWREG(AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_XOSCHFCTL) = DDI_0_OSC_XOSCHFCTL_TCXO_MODE_XOSC_HF_EN |
1372 DDI_0_OSC_XOSCHFCTL_TCXO_MODE;
1373 }
1374 else {
1375 HWREG(AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_XOSCHFCTL) = DDI_0_OSC_XOSCHFCTL_TCXO_MODE;
1376 }
1377
1378 /* Wait for ~10 us for common mode bias to stabilise and clock
1379 * qual to take affect.
1380 */
1381 delayUs(TCXO_RAMP_DELAY);
1382
1383 /* Enable power on TCXO */
1384 (*(PowerCC26X2_config.enableTCXOFxn))(true);
1385
1386 /* Start clock to wait for TCXO startup time before clock switch
1387 * can be attempted.
1388 */
1389 ClockP_start(ClockP_handle(&PowerCC26X2_module.tcxoEnableClock));
1390 }
1391 else {
1392 /* Turn on and request XOSC_HF from the hardware for regular
1393 * XOSC and HPOSC. TCXO does not require this call until right
1394 * before switching since we do not rely on the harware to
1395 * interrupt the system once the XOSC is stable.
1396 */
1397 OSCHF_TurnOnXosc();
1398 }
1399
1400 PowerCC26X2_module.xoscPending = true;
1401
1402 /* Unless it is disallowed, unmask the XOSC_HF ready to switch flag */
1403 if (!((CCFGRead_XOSC_FREQ() == CCFGREAD_XOSC_FREQ_TCXO) &&
1404 (PowerCC26X2_config.enableTCXOFxn != NULL)))
1405 {
1406 if (!(Power_getConstraintMask() & (1 << PowerCC26XX_SWITCH_XOSC_HF_MANUALLY))) {
1407
1408 /* Clearing the flag in the ISR does not always work. Clear it
1409 * again just in case
1410 * */
1411 HWREG(PRCM_BASE + PRCM_O_OSCICR) = PRCM_OSCICR_HFSRCPENDC_M;
1412
1413 /* Turn on oscillator interrupt for SCLK_HF switching */
1414 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_HFSRCPENDIM_M;
1415 }
1416 }
1417
1418 /* If the device goes into IDLE in between turning on XOSC_HF and
1419 * and switching SCLK_HF to XOSC_HF, the INT_OSC_COMB HFSRCPEND
1420 * trigger will be suppressed.
1421 * The DISALLOW_IDLE constraint should only ever be set whenever
1422 * we transition from xoscPending == false to true.
1423 */
1424 Power_setConstraint(PowerCC26XX_DISALLOW_IDLE);
1425 }
1426
1427 /* when release XOSC_HF, auto switch to RCOSC_HF */
1428 else if (action == PowerCC26XX_DISABLE) {
1429 OSCHF_SwitchToRcOscTurnOffXosc();
1430
1431 /* Handle TCXO if selected in CCFG */
1432 if ((CCFGRead_XOSC_FREQ() == CCFGREAD_XOSC_FREQ_TCXO) &&
1433 (PowerCC26X2_config.enableTCXOFxn != NULL)) {
1434 /* Disable Clock in case we have started it and are waiting for
1435 * the TCXO to stabilise.
1436 * If the Clock is not currently active, this should do nothing.
1437 */
1438 ClockP_stop(ClockP_handle(&PowerCC26X2_module.tcxoEnableClock));
1439
1440 /* Disable clock qualification on 48MHz signal from TCXO and turn
1441 * off TCXO bypass.
1442 * If we do not disable clock qualificaition, it will not run the
1443 * next time we switch to TCXO.
1444 */
1445 if (CCFGRead_TCXO_TYPE() == 1) {
1446 /* Also turn off bias if clipped sine TCXO type. The bias
1447 * consumes a few hundred uA. That is fine while the TCXO is
1448 * running but we should not incur this penalty when not running
1449 * on TCXO.
1450 */
1451 HWREG(AUX_DDI0_OSC_BASE + DDI_O_CLR + DDI_0_OSC_O_XOSCHFCTL) = DDI_0_OSC_XOSCHFCTL_TCXO_MODE |
1452 DDI_0_OSC_XOSCHFCTL_BYPASS |
1453 DDI_0_OSC_XOSCHFCTL_TCXO_MODE_XOSC_HF_EN;
1454 }
1455 else {
1456 HWREG(AUX_DDI0_OSC_BASE + DDI_O_CLR + DDI_0_OSC_O_XOSCHFCTL) = DDI_0_OSC_XOSCHFCTL_TCXO_MODE |
1457 DDI_0_OSC_XOSCHFCTL_BYPASS;
1458 }
1459
1460 /* Check if function for enabling/disabling TCXO is supported */
1461 if (PowerCC26X2_config.enableTCXOFxn != NULL) {
1462
1463 /* Disable TCXO by turning off power */
1464 (*(PowerCC26X2_config.enableTCXOFxn))(false);
1465 }
1466 }
1467
1468 /* If we have not actually switched to XOSC_HF yet, we need to
1469 * undo what we did above when turning on XOSC_HF. Otherwise,
1470 * we may not balance the constraints correctly or get
1471 * unexpected interrupts.
1472 */
1473 if (PowerCC26X2_module.xoscPending) {
1474 /* Remove HFSRCPEND from the OSC_COMB interrupt mask */
1475 uint32_t oscMask = HWREG(PRCM_BASE + PRCM_O_OSCIMSC);
1476 HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = oscMask & ~ PRCM_OSCIMSC_HFSRCPENDIM_M;
1477
1478 /* Clear any residual trigger for HFSRCPEND */
1479 HWREG(PRCM_BASE + PRCM_O_OSCICR) = PRCM_OSCICR_HFSRCPENDC;
1480
1481 Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE);
1482
1483 PowerCC26X2_module.xoscPending = false;
1484 }
1485 }
1486 return (0);
1487 }
1488
1489 /*
1490 * ======== switchToTCXO ========
1491 * Switching to TCXO after TCXO startup time has expired.
1492 */
switchToTCXO(void)1493 static void switchToTCXO(void)
1494 {
1495 /* Set bypass bit */
1496 HWREG(AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_XOSCHFCTL) = DDI_0_OSC_XOSCHFCTL_BYPASS;
1497
1498 /* Request XOSC_HF. In this instance, that is the TCXO and it will
1499 * immediately be ready to switch to after requesting since we turned it on
1500 * earlier with a GPIO and waited for it to stabilise.
1501 */
1502 OSCHF_TurnOnXosc();
1503
1504 /* Switch to TCXO */
1505 switchXOSCHF();
1506 }
1507
1508