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