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