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