1 /*
2  * Copyright (c) 2021-2024, 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  *  ======== PowerCC23X0.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/DebugP.h>
41 
42 #include <ti/drivers/Power.h>
43 #include <ti/drivers/Temperature.h>
44 #include <ti/drivers/GPIO.h>
45 
46 #include <ti/drivers/utils/Math.h>
47 
48 #include <ti/log/Log.h>
49 
50 #include <ti/devices/DeviceFamily.h>
51 #include DeviceFamily_constructPath(inc/hw_types.h)
52 #include DeviceFamily_constructPath(inc/hw_memmap.h)
53 #include DeviceFamily_constructPath(inc/hw_ints.h)
54 #include DeviceFamily_constructPath(inc/hw_clkctl.h)
55 #include DeviceFamily_constructPath(inc/hw_evtsvt.h)
56 #include DeviceFamily_constructPath(inc/hw_evtull.h)
57 #include DeviceFamily_constructPath(inc/hw_ckmd.h)
58 #include DeviceFamily_constructPath(inc/hw_rtc.h)
59 #include DeviceFamily_constructPath(inc/hw_systim.h)
60 #include DeviceFamily_constructPath(inc/hw_pmctl.h)
61 #include DeviceFamily_constructPath(inc/hw_pmud.h)
62 #include DeviceFamily_constructPath(inc/hw_sys0.h)
63 #include DeviceFamily_constructPath(inc/hw_ioc.h)
64 #include DeviceFamily_constructPath(driverlib/cpu.h)
65 #include DeviceFamily_constructPath(driverlib/ckmd.h)
66 #include DeviceFamily_constructPath(driverlib/hapi.h)
67 #include DeviceFamily_constructPath(driverlib/gpio.h)
68 #include DeviceFamily_constructPath(driverlib/lrfd.h)
69 #include DeviceFamily_constructPath(driverlib/pmctl.h)
70 #include DeviceFamily_constructPath(cmsis/core/cmsis_compiler.h)
71 
72 /* Forward declarations */
73 int_fast16_t PowerCC23X0_notify(uint_fast16_t eventType);
74 static void PowerCC23X0_oscillatorISR(uintptr_t arg);
75 static void PowerCC23X0_enterStandby(void);
76 static void PowerCC23X0_setDependencyCount(Power_Resource resourceId, uint8_t count);
77 bool PowerCC23X0_isValidResourceId(Power_Resource resourceId);
78 static void PowerCC23X0_startHFXT(void);
79 static void PowerCC23X0_hfxtCompensateFxn(int16_t currentTemperature,
80                                           int16_t thresholdTemperature,
81                                           uintptr_t clientArg,
82                                           Temperature_NotifyObj *notifyObject);
83 static uint32_t PowerCC23X0_temperatureToRatio(int16_t temperature);
84 static void PowerCC23X0_updateHFXTRatio(uint32_t ratio);
85 
86 static void PowerCC23X0_hfxtAmpsettledTimeout(uintptr_t arg);
87 static void PowerCC23X0_initialHfxtAmpCompClockCb(uintptr_t searchDone);
88 static void PowerCC23X0_forceHfxtFsmToRamp1(void);
89 static void PowerCC23X0_startContHfxtAmpMeasurements(void);
90 static void PowerCC23X0_stopContHfxtAmpMeasurements(void);
91 static uint32_t PowerCC23X0_getHfxtAmpMeasurement(void);
92 
93 /* Externs */
94 extern const PowerCC23X0_Config PowerCC23X0_config;
95 extern const uint_least8_t GPIO_pinLowerBound;
96 extern const uint_least8_t GPIO_pinUpperBound;
97 extern const uint_least8_t PowerLPF3_extlfPin;
98 extern const uint_least8_t PowerLPF3_extlfPinMux;
99 
100 /* Macro for weak definition of the Power Log module */
101 Log_MODULE_DEFINE_WEAK(LogModule_Power, {0});
102 
103 /* Function Macros */
104 
105 /* We need to define the IOC_BASE_PIN_REG address ourselves since IOC_O_IOC0
106  * does not exist on CC2340R2
107  */
108 #define IOC_BASE_PIN_REG 0x00000100
109 #define IOC_ADDR(index)  (IOC_BASE + IOC_BASE_PIN_REG + (sizeof(uint32_t) * index))
110 
111 /* Macro used to extract the resource group  from a resource ID */
112 #define RESOURCE_GROUP(resourceId) ((resourceId)&PowerCC23X0_PERIPH_GROUP_M)
113 
114 /* Macro used to extract the bit index shift encoded from a resource ID */
115 #define RESOURCE_BIT_INDEX(resourceId) ((resourceId)&PowerCC23X0_PERIPH_BIT_INDEX_M)
116 
117 #define HFXT_COMP_MAX_TEMP (125)
118 #define HFXT_COMP_MIN_TEMP (-40)
119 
120 /* Timeout value used to detect if HFXT FSM is stuck in RAMP0 state */
121 #define HFXT_AMP_COMP_START_TIMEOUT_US 500
122 
123 /* Time to wait after changing HFXTTARG.IREF and measuring the resulting
124  * HFXT amplitude
125  */
126 #define HFXT_AMP_COMP_MEASUREMENT_US 1000
127 
128 /* The limits for the allowed target IREF values to be passed to
129  * CKMDSetTargetIrefTrim()
130  */
131 #define HFXT_TARGET_IREF_MAX 8
132 #define HFXT_TARGET_IREF_MIN 3
133 
134 /* Type definitions */
135 
136 /*! Type used for passing configuration information to HFXT temperature
137  * notification callback
138  */
139 typedef union
140 {
141     struct
142     {
143         int16_t delta;
144         int16_t threshold;
145     } temperature;
146     uint32_t value;
147 } PowerCC23X0_hfxtConfig;
148 
149 /* Static Globals */
150 
151 /*! Temperature notification to compensate the HFXT */
152 static Temperature_NotifyObj PowerCC23X0_hfxtCompNotifyObj = {0};
153 
154 /*! Temperature compensation coefficients for HFXT */
155 static struct
156 {
157     int32_t P0;
158     int32_t P1;
159     int32_t P2;
160     int32_t P3;
161     uint8_t shift;
162 } PowerCC23X0_hfxtCompCoefficients;
163 
164 /* Global state variable to track if HFXT compensation is enabled or not.
165  * It is used to check whether temperature notifications should be re-enabled
166  * after an update or not, in case compensation has been asynchronously disabled
167  */
168 static bool PowerCC23X0_hfxtCompEnabled = false;
169 
170 /* Array to maintain constraint reference counts */
171 static uint8_t constraintCounts[PowerCC23X0_NUMCONSTRAINTS];
172 
173 /* Mask of Power constraints for quick access */
174 static uint32_t constraintMask = 0;
175 
176 /* Arrays to maintain resource dependency reference counts.
177  * Each resource group will have an array associated with it, and the arrays can
178  * be indexed using the bit index shift value encoded in the resource ID.
179  */
180 static uint8_t resourceCountsClkctl0[PowerCC23X0_NUMRESOURCES_CLKCTL0];
181 static uint8_t resourceCountsLrfd[PowerCC23X0_NUMRESOURCES_LRFD];
182 
183 /* Keeps track of the configured Power policy. Power_idleFunc() will not run
184  * the policy if this is set to NULL
185  */
186 static Power_PolicyFxn policyFxn = NULL;
187 
188 /* Is the Power policy enabled? */
189 static bool isPolicyEnabled = false;
190 
191 /* Has the Power driver been initialized */
192 static bool isInitialized = false;
193 
194 /* Power state of the system. Idle, active, standby, etc */
195 static uint8_t powerState = Power_ACTIVE;
196 
197 /* Event notification list */
198 static List_List notifyList;
199 
200 /* Interrupt for handling clock switching */
201 static HwiP_Struct ckmHwi;
202 
203 /* Function to be called to start the initial HFXT Amplitude compensation on the
204  * next AMPSETTLED interrupt.
205  * Will be NULL if initial compensation should not be
206  * be performed. I.e. if it has already been performed or if it is not enabled.
207  */
208 static PowerLPF3_StartInitialHfxtAmpCompFxn startInitialHfxtAmpCompFxn = NULL;
209 
210 /* Clock object used by HFXT amplitude compensation. It is reused for multiple
211  * purposes. The callback function of the ClockP object will be changed
212  * dynamically, depending on the use case. The different use cases are described
213  * below:
214  * - Currently waiting for the AMPSETTLED interrupt, after starting HFXT
215  *   - Callback function: PowerCC23X0_hfxtAmpsettledTimeout
216  *   - Used for timeout to detect if HFXT FSM gets stuck in RAMP0 state
217  * - Currently performing the initial HFXT amplitude compensation, where we are
218  *   doing a full search for the optimal HFXTTARG.IREF value,
219  *   after the AMPSETTLED signal and before the PowerLPF3_HFXT_AVAILABLE
220  *   notification.
221  *   - Callback function: PowerCC23X0_initialHfxtAmpCompClockCb
222  *   - The clock is used to schedule measurements and evaluations of the HFXT
223  *     amplitude.
224  * - After PowerLPF3_HFXT_AVAILABLE notification:
225  *   - Callback function: NULL
226  *   - Currently not used. Could potentially be used to periodically check if
227  *     amplitude adjustments are needed.
228  *
229  * Dynamically changing the callback function is done instead of having a common
230  * callback function, to be able to only link the functions that are actually
231  * used. For example, if the initial HFXT amplitude compensation at boot is not
232  * enabled, then there will be no reference to
233  * PowerLPF3_startInitialHfxtAmpComp() which is the only place that references
234  * PowerCC23X0_initialHfxtAmpCompClockCb(), and therefore neither functions
235  * will be linked in the application.
236  */
237 static ClockP_Struct hfxtAmpCompClock;
238 
239 /* Non-static Globals */
240 
241 /* Interrupt for ClockP and Power policy */
242 HwiP_Struct clockHwi;
243 
244 /* ****************** Power APIs ******************** */
245 
246 /*
247  *  ======== Power_init ========
248  */
Power_init()249 int_fast16_t Power_init()
250 {
251     /* If this function has already been called, just return */
252     if (isInitialized)
253     {
254         return Power_SOK;
255     }
256 
257     isInitialized = true;
258 
259     isPolicyEnabled = PowerCC23X0_config.policyFxn != NULL;
260 
261     policyFxn = PowerCC23X0_config.policyFxn;
262 
263     startInitialHfxtAmpCompFxn = PowerCC23X0_config.startInitialHfxtAmpCompFxn;
264 
265     /* Construct the CKM hwi responsible for oscillator related events.
266      * Since there is no dedicated CKM interrupt line, we need to mux one of
267      * the configurable lines to the CKM.
268      * CPUIRQ3 is dedicated to this purpose.
269      */
270     HwiP_construct(&ckmHwi, INT_CPUIRQ3, PowerCC23X0_oscillatorISR, NULL);
271 
272     HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ3SEL) = EVTSVT_CPUIRQ3SEL_PUBID_AON_CKM_COMB;
273 
274     /* Enable a selection of CKM signals as interrupt sources. For now,
275      * we will stick to AMPSETTLED since it is related to existing notification
276      * and HFXTFAULT and TRACKREFLOSS to be able to handle HFXT clock loss.
277      * TRACKREFLOSS may occur when entering and exiting fake standby with the
278      * debugger attached.
279      */
280     HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMSET_AMPSETTLED | CKMD_IMSET_HFXTFAULT | CKMD_IMSET_TRACKREFLOSS;
281 
282     HwiP_enableInterrupt(INT_CPUIRQ3);
283 
284     /* Use RTC channel 0 in compare mode. Channel 1 could be used for other
285      * purposes.
286      */
287     HWREG(RTC_BASE + RTC_O_IMSET) = RTC_IMSET_EV0_SET;
288 
289     /* Configure RTC to halt when CPU stopped during debug */
290     HWREG(RTC_BASE + RTC_O_EMU) = RTC_EMU_HALT_STOP;
291 
292     /* Configure SysTimer to halt when CPU stopped during debug. The setting
293      * is sync'd from RTC_EMU on each wakeup from standby.
294      */
295     HWREG(SYSTIM_BASE + SYSTIM_O_EMU) = SYSTIM_EMU_HALT_STOP;
296 
297     /* Disable automatic periodic adjustments of HFXT amplitude. It will instead
298      * be done in SW.
299      * With this setting it is not necessary to restart HFXT after changing
300      * IREF.
301      */
302     HWREG(CKMD_BASE + CKMD_O_AMPCFG1) &= ~CKMD_AMPCFG1_INTERVAL_M;
303 
304     /* Set target HFXT IREF to the max value, to ensure it is high enough.
305      * It will be gradually updated as needed later, as part of the HFXT
306      * amplitude compensation.
307      */
308     CKMDSetTargetIrefTrim(HFXT_TARGET_IREF_MAX);
309 
310     /* Construct HFXT amplitude compensation clock.
311      * At boot, the clock is used to detect if the HFXT FSM gets stuck in the
312      * RAMP0 state.
313      */
314     ClockP_construct(&hfxtAmpCompClock,
315                      PowerCC23X0_hfxtAmpsettledTimeout,
316                      HFXT_AMP_COMP_START_TIMEOUT_US / ClockP_getSystemTickPeriod(),
317                      NULL);
318 
319     /* Start HFXT */
320     PowerCC23X0_startHFXT();
321 
322     /* Start timeout clock.
323      * Note, interrupts are guaranteed to be disabled during Power_init(), so
324      * there is no risk of the AMPSETTLED callback stopping the clock before it
325      * is started.
326      */
327     ClockP_start(&hfxtAmpCompClock);
328 
329     /* Enable tracking loop with HFXT as reference. This will automatically
330      * calibrate LFOSC against HFXT whenever HFXT is enabled; usually after
331      * waking up from standby.
332      * This is needed to ensure fast HFXT startup and a reasonably accurate
333      * LFOSC frequency.
334      */
335     HWREG(CKMD_BASE + CKMD_O_HFTRACKCTL) |= CKMD_HFTRACKCTL_EN_M | CKMD_HFTRACKCTL_REFCLK_HFXT;
336 
337     /* Enable GPIO and RTC standby wakeup sources */
338     HWREG(EVTULL_BASE + EVTULL_O_WKUPMASK) = EVTULL_WKUPMASK_AON_IOC_COMB_M | EVTULL_WKUPMASK_AON_RTC_COMB_M;
339 
340     return Power_SOK;
341 }
342 
343 /*
344  *  ======== Power_disablePolicy ========
345  *  Do not run the configured policy
346  */
Power_disablePolicy(void)347 bool Power_disablePolicy(void)
348 {
349     bool wasPolicyEnabled = isPolicyEnabled;
350 
351     isPolicyEnabled = false;
352 
353     return wasPolicyEnabled;
354 }
355 
356 /*
357  *  ======== Power_enablePolicy ========
358  *  Run the configured policy
359  */
Power_enablePolicy(void)360 void Power_enablePolicy(void)
361 {
362     isPolicyEnabled = true;
363 }
364 
365 /*
366  *  ======== Power_setPolicy ========
367  *  Set the Power policy function
368  */
Power_setPolicy(Power_PolicyFxn policy)369 void Power_setPolicy(Power_PolicyFxn policy)
370 {
371     policyFxn = policy;
372 }
373 
374 /*
375  *  ======== Power_getConstraintMask ========
376  *  Get a bitmask indicating the constraints that have been registered with
377  *  Power.
378  */
Power_getConstraintMask(void)379 uint_fast32_t Power_getConstraintMask(void)
380 {
381     return constraintMask;
382 }
383 
384 /*
385  *  ======== Power_getDependencyCount ========
386  *  Get the count of dependencies that are currently declared upon a resource.
387  */
Power_getDependencyCount(Power_Resource resourceId)388 int_fast16_t Power_getDependencyCount(Power_Resource resourceId)
389 {
390     DebugP_assert(PowerCC23X0_isValidResourceId(resourceId));
391 
392     uint8_t bitIndex    = RESOURCE_BIT_INDEX(resourceId);
393     uint_fast16_t group = RESOURCE_GROUP(resourceId);
394     if (group == PowerCC23X0_PERIPH_GROUP_CLKCTL0)
395     {
396         return (int_fast16_t)resourceCountsClkctl0[bitIndex];
397     }
398     else if (group == PowerCC23X0_PERIPH_GROUP_LRFD)
399     {
400         return (int_fast16_t)resourceCountsLrfd[bitIndex];
401     }
402 
403     return (int_fast16_t)Power_EINVALIDINPUT;
404 }
405 
406 /*
407  *  ======== Power_getConstraintCount ========
408  *  Get the count of constraints that are currently set on a certain
409  *  operational transition
410  */
Power_getConstraintCount(uint_fast16_t constraintId)411 int_fast16_t Power_getConstraintCount(uint_fast16_t constraintId)
412 {
413     DebugP_assert(constraintId < PowerCC23X0_NUMCONSTRAINTS);
414 
415     if (constraintId < PowerCC23X0_NUMCONSTRAINTS)
416     {
417         return (int_fast16_t)constraintCounts[constraintId];
418     }
419     else
420     {
421         return (int_fast16_t)Power_EINVALIDINPUT;
422     }
423 }
424 
425 /*
426  *  ======== Power_getTransitionLatency ========
427  *  Get the transition latency for a sleep state.  The latency is reported
428  *  in units of microseconds.
429  */
Power_getTransitionLatency(uint_fast16_t sleepState,uint_fast16_t type)430 uint_fast32_t Power_getTransitionLatency(uint_fast16_t sleepState, uint_fast16_t type)
431 {
432     uint32_t latency = 0;
433 
434     if (type == Power_RESUME)
435     {
436         if (sleepState == PowerLPF3_STANDBY)
437         {
438             latency = PowerCC23X0_RESUMETIMESTANDBY;
439         }
440     }
441     else
442     {
443         if (sleepState == PowerLPF3_STANDBY)
444         {
445             latency = PowerCC23X0_TOTALTIMESTANDBY;
446         }
447     }
448 
449     return latency;
450 }
451 
452 /*
453  *  ======== Power_getTransitionState ========
454  *  Get the current sleep transition state.
455  */
Power_getTransitionState(void)456 uint_fast16_t Power_getTransitionState(void)
457 {
458     return powerState;
459 }
460 
461 /*
462  *  ======== Power_idleFunc ========
463  *  Function needs to be plugged into the idle loop.
464  *  It calls the configured policy function if it is not NULL.
465  */
Power_idleFunc()466 void Power_idleFunc()
467 {
468     if (policyFxn != NULL && isPolicyEnabled == true)
469     {
470         (*(policyFxn))();
471     }
472 }
473 
474 /*
475  *  ======== Power_registerNotify ========
476  *  Register a function to be called on a specific power event.
477  */
Power_registerNotify(Power_NotifyObj * notifyObj,uint_fast16_t eventTypes,Power_NotifyFxn notifyFxn,uintptr_t clientArg)478 int_fast16_t Power_registerNotify(Power_NotifyObj *notifyObj,
479                                   uint_fast16_t eventTypes,
480                                   Power_NotifyFxn notifyFxn,
481                                   uintptr_t clientArg)
482 {
483     int_fast16_t status = Power_SOK;
484 
485     /* Check for NULL pointers */
486     if ((notifyObj == NULL) || (notifyFxn == NULL))
487     {
488         Log_printf(LogModule_Power,
489                    Log_WARNING,
490                    "Power_registerNotify: Notify registration failed due to NULL pointer");
491 
492         status = Power_EINVALIDPOINTER;
493     }
494     else
495     {
496         notifyObj->eventTypes = eventTypes;
497         notifyObj->notifyFxn  = notifyFxn;
498         notifyObj->clientArg  = clientArg;
499 
500         Log_printf(LogModule_Power,
501                    Log_INFO,
502                    "Power_registerNotify: Register fxn at address 0x%x with event types 0x%x and clientArg 0x%x",
503                    notifyFxn,
504                    eventTypes,
505                    clientArg);
506 
507         /* Place notify object on event notification queue. Assume that
508          * List_Elem struct member is the first struct member in
509          * Power_NotifyObj.
510          */
511         List_put(&notifyList, (List_Elem *)notifyObj);
512     }
513 
514     return status;
515 }
516 
517 /*
518  *  ======== Power_unregisterNotify ========
519  *  Unregister for a power notification.
520  *
521  */
Power_unregisterNotify(Power_NotifyObj * notifyObj)522 void Power_unregisterNotify(Power_NotifyObj *notifyObj)
523 {
524     Log_printf(LogModule_Power,
525                Log_INFO,
526                "Power_unregisterNotify: Unregister fxn at address 0x%x with event types 0x%x and clientArg 0x%x",
527                notifyObj->notifyFxn,
528                notifyObj->eventTypes,
529                notifyObj->clientArg);
530 
531     /* Remove notify object from its event queue */
532     List_remove(&notifyList, (List_Elem *)notifyObj);
533 }
534 
535 /*
536  *  ======== Power_setConstraint ========
537  *  Declare an operational constraint.
538  */
Power_setConstraint(uint_fast16_t constraintId)539 int_fast16_t Power_setConstraint(uint_fast16_t constraintId)
540 {
541     uintptr_t key;
542 
543     DebugP_assert(constraintId < PowerCC23X0_NUMCONSTRAINTS);
544 
545     key = HwiP_disable();
546 
547     /* Set the specified constraint in the constraintMask for faster access */
548     constraintMask |= 1 << constraintId;
549 
550     /* Increment the specified constraint count */
551     constraintCounts[constraintId]++;
552 
553     HwiP_restore(key);
554 
555     return Power_SOK;
556 }
557 
558 /*
559  *  ======== Power_releaseConstraint ========
560  *  Release a previously declared constraint.
561  */
Power_releaseConstraint(uint_fast16_t constraintId)562 int_fast16_t Power_releaseConstraint(uint_fast16_t constraintId)
563 {
564     uintptr_t key;
565 
566     DebugP_assert(constraintId < PowerCC23X0_NUMCONSTRAINTS);
567 
568     key = HwiP_disable();
569 
570     DebugP_assert(constraintCounts[constraintId] != 0);
571 
572     constraintCounts[constraintId]--;
573 
574     /* Only update the constraint mask if we removed the constraint entirely */
575     if (constraintCounts[constraintId] == 0)
576     {
577         constraintMask &= ~(1 << constraintId);
578     }
579 
580     HwiP_restore(key);
581 
582     return Power_SOK;
583 }
584 
585 /*
586  *  ======== Power_setDependency ========
587  *  Declare a dependency upon a resource.
588  */
Power_setDependency(Power_Resource resourceId)589 int_fast16_t Power_setDependency(Power_Resource resourceId)
590 {
591     uint8_t previousCount;
592     uintptr_t key;
593     uint8_t bitIndex;
594 
595     DebugP_assert(PowerCC23X0_isValidResourceId(resourceId));
596 
597     key = HwiP_disable();
598 
599     /* Buffer previous reference count */
600     previousCount = Power_getDependencyCount(resourceId);
601 
602     /* Increment reference count */
603     PowerCC23X0_setDependencyCount(resourceId, previousCount + 1);
604 
605     /* If the resource was NOT activated previously ... */
606     if (previousCount == 0)
607     {
608         bitIndex = RESOURCE_BIT_INDEX(resourceId);
609         /* Turn on the peripheral */
610         switch (RESOURCE_GROUP(resourceId))
611         {
612             case PowerCC23X0_PERIPH_GROUP_CLKCTL0:
613                 HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0) = 1 << bitIndex;
614                 break;
615             case PowerCC23X0_PERIPH_GROUP_LRFD:
616                 LRFDSetClockDependency(1 << bitIndex, LRFD_CLK_DEP_POWER);
617                 break;
618             default:
619                 break;
620         }
621     }
622 
623     Log_printf(LogModule_Power,
624                Log_INFO,
625                "Power_setDependency: Updated resource counter = %d for resource ID = 0x%x",
626                Power_getDependencyCount(resourceId), resourceId);
627 
628     HwiP_restore(key);
629 
630     return Power_SOK;
631 }
632 
633 /*
634  *  ======== Power_releaseDependency ========
635  *  Release a previously declared dependency.
636  */
Power_releaseDependency(Power_Resource resourceId)637 int_fast16_t Power_releaseDependency(Power_Resource resourceId)
638 {
639     uint8_t resourceCount;
640     uintptr_t key;
641     uint8_t bitIndex;
642 
643     DebugP_assert(PowerCC23X0_isValidResourceId(resourceId));
644 
645     key = HwiP_disable();
646 
647     resourceCount = Power_getDependencyCount(resourceId);
648 
649     DebugP_assert(resourceCount != 0);
650 
651     /* Decrement the reference count */
652     resourceCount--;
653     PowerCC23X0_setDependencyCount(resourceId, resourceCount);
654 
655     /* If this was the last dependency being released.. */
656     if (resourceCount == 0)
657     {
658         bitIndex = RESOURCE_BIT_INDEX(resourceId);
659         /* Turn off the peripheral */
660         switch (RESOURCE_GROUP(resourceId))
661         {
662             case PowerCC23X0_PERIPH_GROUP_CLKCTL0:
663                 HWREG(CLKCTL_BASE + CLKCTL_O_CLKENCLR0) = 1 << bitIndex;
664                 break;
665             case PowerCC23X0_PERIPH_GROUP_LRFD:
666                 LRFDReleaseClockDependency(1 << bitIndex, LRFD_CLK_DEP_POWER);
667                 break;
668             default:
669                 break;
670         }
671     }
672 
673     Log_printf(LogModule_Power,
674                Log_INFO,
675                "Power_releaseDependency: Updated resource counter = %d for resource ID = 0x%x",
676                Power_getDependencyCount(resourceId), resourceId);
677 
678     HwiP_restore(key);
679 
680     return Power_SOK;
681 }
682 
683 /*
684  *  ======== Power_shutdown ========
685  */
Power_shutdown(uint_fast16_t shutdownState,uint_fast32_t shutdownTime)686 int_fast16_t Power_shutdown(uint_fast16_t shutdownState, uint_fast32_t shutdownTime)
687 {
688     int_fast16_t notifyStatus;
689     uint32_t hwiKey;
690     uint8_t i;
691     bool ioPending = false;
692 
693     hwiKey = HwiP_disable();
694 
695     /* Check if there is a constraint to prohibit shutdown */
696     if (Power_getConstraintMask() & (1 << PowerLPF3_DISALLOW_SHUTDOWN))
697     {
698         HwiP_restore(hwiKey);
699         return Power_ECHANGE_NOT_ALLOWED;
700     }
701 
702     /* Check whether we were transitioning to another power state */
703     if (powerState != Power_ACTIVE)
704     {
705         HwiP_restore(hwiKey);
706         return Power_EBUSY;
707     }
708 
709     /* Set new transition state to entering shutdown */
710     powerState = Power_ENTERING_SHUTDOWN;
711 
712     /* Signal all clients registered for pre-shutdown notification */
713     notifyStatus = PowerCC23X0_notify(PowerLPF3_ENTERING_SHUTDOWN);
714 
715     for (i = GPIO_pinLowerBound; i <= GPIO_pinUpperBound; i++)
716     {
717         /* Read WUCFGSD field once and check both values.
718          * Use IOC3 since CC2340R2 does not have IOC0, IOC1, or IOC2.
719          */
720         uint32_t ioShutdownConfig = HWREG(IOC_ADDR(i)) & IOC_IOC3_WUCFGSD_M;
721 
722         if ((ioShutdownConfig == IOC_IOC3_WUCFGSD_WAKE_HIGH || ioShutdownConfig == IOC_IOC3_WUCFGSD_WAKE_LOW) &&
723             GPIOGetEventDio(i))
724         {
725             ioPending = true;
726         }
727     }
728 
729     /* If no IO event is pending on a shutdown wakeup IO, go to shutdown */
730     if (ioPending == false && notifyStatus == Power_SOK)
731     {
732         HWREG(PMCTL_BASE + PMCTL_O_SHTDWN) = PMCTL_SHTDWN_KEY_VALID;
733     }
734 
735     /* If shutdown succeeded, should never get here */
736 
737     powerState = Power_ACTIVE;
738 
739     HwiP_restore(hwiKey);
740 
741     /* If we get here, failed to shutdown, return error code */
742     return Power_EFAIL;
743 }
744 
745 /*
746  *  ======== Power_sleep ========
747  */
Power_sleep(uint_fast16_t sleepState)748 int_fast16_t Power_sleep(uint_fast16_t sleepState)
749 {
750     int_fast16_t status = Power_SOK;
751 
752     /* Signal all clients registered for pre standby wakeup notification */
753     status = PowerCC23X0_notify(PowerLPF3_ENTERING_STANDBY);
754 
755     /* Check for any error */
756     if (status != Power_SOK)
757     {
758         powerState = Power_ACTIVE;
759 
760         Log_printf(LogModule_Power,
761                    Log_WARNING,
762                    "Power_sleep: Entering standby failed with status = 0x%x",
763                    status);
764 
765         return status;
766     }
767 
768     /* Adjust HFXT amplitude if needed */
769     int_fast8_t adjustment = PowerLPF3_getHfxtAmpAdjustment();
770     if (adjustment != 0)
771     {
772         PowerLPF3_adjustHfxtAmp(adjustment);
773 
774         /* Explicitly turn off HFXT before entering standby. This is to ensure
775          * that the HFXT is actually re-started with the new amplitude. In
776          * some cases the HFXT will not be turned off when entering standby. For
777          * example when entering fake standby with the debugger attached.
778          */
779         HWREG(CKMD_BASE + CKMD_O_HFXTCTL) &= ~CKMD_HFXTCTL_EN_M;
780     }
781 
782     /* Stop continuous measurements of HFXT amplitude before entering standby,
783      * since the same hardware is needed to start the HFXT when waking up from
784      * standby.
785      */
786     PowerCC23X0_stopContHfxtAmpMeasurements();
787 
788     /* Call wrapper function to ensure that R0-R3 are saved and restored before
789      * and after this function call. Otherwise, compilers will attempt to stash
790      * values on the stack while on the PSP and then restore them just after
791      * HapiEnterStandby() on the MSP. Which will cause wildly unexpected
792      * behaviour.
793      */
794     PowerCC23X0_enterStandby();
795 
796     /* Now that we have returned and are executing code from flash again, start
797      * up the HFXT using the workaround for the HFXT amplitude control ADC bias
798      * point
799      */
800     PowerCC23X0_startHFXT();
801 
802     /* Now clear the transition state before re-enabling the scheduler */
803     powerState = Power_ACTIVE;
804 
805     return status;
806 }
807 
808 /*
809  *  ======== Power_reset ========
810  */
Power_reset(void)811 void Power_reset(void)
812 {
813     PMCTLResetSystem();
814 }
815 
816 /*
817  *  ======== PowerCC23X0_doWFI ========
818  */
PowerCC23X0_doWFI(void)819 void PowerCC23X0_doWFI(void)
820 {
821     __WFI();
822 }
823 
824 /*
825  *  ======== PowerCC23X0_enterStandby ========
826  *  Wrapper function to ensure that R0-R3 are saved and restored before and
827  *  after this function call. Otherwise, compilers will attempt to stash
828  *  values on the stack while on the PSP and then restore them just after
829  *  HapiEnterStandby() on the MSP. Which will cause wildly unexpected behaviour.
830  */
PowerCC23X0_enterStandby(void)831 void PowerCC23X0_enterStandby(void)
832 {
833     /* Declare static volatile variable to ensure the toolchain does not use
834      * stack for the variable and does not optimize this memory allocation
835      * away.
836      */
837     static volatile uint32_t controlPreStandby;
838 
839     /* Clear all CKM LDO SW control bits. If we do not do this before entering
840      * standby, the LDO will remain on in standby and consume power. We do not
841      * disable it earlier after turning on HFXT to avoid waiting 20us to safely
842      * disable it.
843      */
844     HWREG(CKMD_BASE + CKMD_O_LDOCTL) = 0x0;
845 
846     /* Stash current CONTROL configuration to re-apply after wakeup.
847      * Depending on the kernel used, we could be on PSP or MSP
848      */
849     controlPreStandby = __get_CONTROL();
850 
851     /* Switch to MSP. HapiEnterStandby() must execute from MSP since the
852      * device reboots into privileged mode on MSP and HapiEnterStandby()
853      * assumes it will be called running on MSP.
854      */
855     __set_CONTROL(0 << 1);
856 
857     /* - Save CPU state on MSP and MSP in CLKCTL_STBYPTR
858      * - Enter standby
859      * - Exit standby
860      * - Restore CPU state from MSP
861      * - Apply copyList
862      */
863     HapiEnterStandby(NULL);
864 
865     /* Switch back to previous stack pointer. */
866     __set_CONTROL(controlPreStandby);
867 }
868 
869 /*
870  *  ======== PowerLPF3_selectLFOSC ========
871  */
PowerLPF3_selectLFOSC(void)872 void PowerLPF3_selectLFOSC(void)
873 {
874     /* Increase LFOSC nanoamp bias current to 150nA to minimise RTN */
875     HWREG(CKMD_BASE + CKMD_O_TRIM1) |= CKMD_TRIM1_NABIAS_LFOSC;
876 
877     /* Select LFOSC */
878     HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFOSC;
879 
880     /* Start LFOSC */
881     HWREG(CKMD_BASE + CKMD_O_LFOSCCTL) = CKMD_LFOSCCTL_EN;
882 
883     /* Disable LFINC filter settling preventing standby */
884     HWREG(CKMD_BASE + CKMD_O_LFINCCTL) &= ~CKMD_LFINCCTL_PREVENTSTBY_M;
885 
886     /* Enable LFCLKGOOD */
887     HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMSET_LFCLKGOOD;
888 
889     /* Disallow standby until LF clock is running. Otherwise, we will only
890      * vector to the ISR after we wake up from standby the next time since the
891      * CKM interrupt is purposefully not configured as a wakeup source.
892      */
893     Power_setConstraint(PowerLPF3_DISALLOW_STANDBY);
894 
895     Log_printf(LogModule_Power, Log_INFO, "PowerLPF3_selectLFOSC: LFOSC selected");
896 }
897 
898 /*
899  *  ======== PowerLPF3_selectLFXT ========
900  */
PowerLPF3_selectLFXT(void)901 void PowerLPF3_selectLFXT(void)
902 {
903     /* Set LFINC override to 32.768 kHz. Will not impact RTC since the fake LF
904      * ticks will have a higher priority than LFINCOVR.
905      *
906      * The value is calculated as period in microseconds with 16 fractional
907      * bits.
908      * The LFXT runs at 32.768 kHz -> 1 / 32768 Hz = 30.5176 us.
909      * 30.5176 * 2^16 = 2000000 = 0x001E8480
910      */
911     HWREG(CKMD_BASE + CKMD_O_LFINCOVR) = 0x001E8480 | CKMD_LFINCOVR_OVERRIDE;
912 
913     /* Set LFCLK  */
914     HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFXT;
915 
916     /* Start LFXT */
917     HWREG(CKMD_BASE + CKMD_O_LFXTCTL) = CKMD_LFXTCTL_EN;
918 
919     /* Enable LFCLKGOOD */
920     HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD;
921 
922     /* Disallow standby until LF clock is running. Otherwise, we will only
923      * vector to the ISR after we wake up from standby the next time since the
924      * CKM interrupt is purposefully not configured as a wakeup source.
925      */
926     Power_setConstraint(PowerLPF3_DISALLOW_STANDBY);
927 
928     Log_printf(LogModule_Power, Log_INFO, "PowerLPF3_selectLFXT: LFXT selected");
929 }
930 
931 /*
932  *  ======== PowerLPF3_selectEXTLF ========
933  */
PowerLPF3_selectEXTLF(void)934 void PowerLPF3_selectEXTLF(void)
935 {
936     /* Set LFINC override to 31.25 kHz.
937      *
938      * The value is calculated as period in microseconds with 16 fractional
939      * bits.
940      * The EXTLF runs at 31.25 kHz -> 1 / 31250 Hz = 32 us.
941      * 32 * 2^16 = 2097152 = 0x00200000
942      */
943     HWREG(CKMD_BASE + CKMD_O_LFINCOVR) = 0x00200000 | CKMD_LFINCOVR_OVERRIDE;
944 
945     /* Set LFCLK to EXTLF */
946     HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_EXTLF;
947 
948     /* Configure EXTLF to the right mux */
949     GPIO_setConfigAndMux(PowerLPF3_extlfPin, GPIO_CFG_INPUT, PowerLPF3_extlfPinMux);
950 
951     /* Enable LFCLKGOOD */
952     HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD;
953 
954     /* Disallow standby until LF clock is running. Otherwise, we will only
955      * vector to the ISR after we wake up from standby the next time since the
956      * CKM interrupt is purposefully not configured as a wakeup source.
957      */
958     Power_setConstraint(PowerLPF3_DISALLOW_STANDBY);
959 
960     Log_printf(LogModule_Power, Log_INFO, "PowerLPF3_selectEXTLF: EXTLF selected");
961 }
962 
963 /*
964  *  ======== PowerCC23X0_oscillatorISR ========
965  */
PowerCC23X0_oscillatorISR(uintptr_t arg)966 static void PowerCC23X0_oscillatorISR(uintptr_t arg)
967 {
968     uint32_t maskedStatus = HWREG(CKMD_BASE + CKMD_O_MIS);
969 
970     /* Manipulating ICLR alone does not actually do anything. The CKM_COMB
971      * signals are almost all level values that reset one cycle after writing to
972      * ICLR. We need to update the mask instead to avoid looping in the ISR
973      */
974     HWREG(CKMD_BASE + CKMD_O_ICLR)  = maskedStatus;
975     HWREG(CKMD_BASE + CKMD_O_IMCLR) = maskedStatus;
976 
977     if (maskedStatus & CKMD_MIS_AMPSETTLED_M)
978     {
979         /* Stop AMPSETTLED timeout clock and change callback function of clock
980          * object (NULL: not used)
981          */
982         ClockP_stop(&hfxtAmpCompClock);
983         ClockP_setFunc(&hfxtAmpCompClock, NULL, 0);
984 
985         /* Start continuous measurements of HFXT amplitude */
986         PowerCC23X0_startContHfxtAmpMeasurements();
987 
988         /* If enabled, start the initial HFXT amplitude compensation */
989         if (startInitialHfxtAmpCompFxn != NULL)
990         {
991             /* Start the asynchronous initial HFXT amplitude compensation.
992              * When the HFXT amplitude compensation is done the
993              * PowerLPF3_HFXT_AVAILABLE notification will be posted, and the
994              * PowerLPF3_DISALLOW_STANDBY constraint will be released.
995              */
996             startInitialHfxtAmpCompFxn();
997 
998             /* Only do the initial HFXT amplitude compensation once */
999             startInitialHfxtAmpCompFxn = NULL;
1000         }
1001         else
1002         {
1003             Log_printf(LogModule_Power, Log_INFO, "PowerCC23X0_oscillatorISR: HFXT is available");
1004 
1005             PowerCC23X0_notify(PowerLPF3_HFXT_AVAILABLE);
1006 
1007             /* Allow standby again now that HFXT has finished starting */
1008             Power_releaseConstraint(PowerLPF3_DISALLOW_STANDBY);
1009         }
1010     }
1011 
1012     if (maskedStatus & (CKMD_MIS_HFXTFAULT_M | CKMD_MIS_TRACKREFLOSS_M))
1013     {
1014         Log_printf(LogModule_Power, Log_WARNING, "PowerCC23X0_oscillatorISR: HFXT fault and/or TRACKREFLOSS, restarting HFXT");
1015 
1016         /* If there was a HFXTFAULT or TRACKREFLOSS, restart HFXT. Consider
1017          * issuing a notification to allow logging. If we keep it like this, we
1018          * could get stuck in an infinite loop restarting a faulty oscillator.
1019          * Then again, it is not like there is a great way to recover from that.
1020          */
1021         HWREG(CKMD_BASE + CKMD_O_HFXTCTL) &= ~CKMD_HFXTCTL_EN_M;
1022 
1023         /* Stop continuous measurements of HFXT amplitude. The hardware used is
1024          * needed to start HFXT.
1025          */
1026         PowerCC23X0_stopContHfxtAmpMeasurements();
1027 
1028         /* Max out IREF */
1029         CKMDSetTargetIrefTrim(HFXT_TARGET_IREF_MAX);
1030 
1031         /* Start up the HFXT using the workaround for the HFXT amplitude control ADC
1032          * bias point
1033          */
1034         PowerCC23X0_startHFXT();
1035 
1036         /* Re-enable interrupts */
1037         HWREG(CKMD_BASE + CKMD_O_IMSET) = maskedStatus & (CKMD_MIS_HFXTFAULT_M | CKMD_MIS_TRACKREFLOSS_M);
1038     }
1039 
1040     if (maskedStatus & CKMD_MIS_TRACKREFLOSS_M)
1041     {
1042         Log_printf(LogModule_Power, Log_WARNING, "PowerCC23X0_oscillatorISR: TRACKREFLOSS, re-enable tracking");
1043 
1044         /* Disable interrupts as HFXT SWTCXO may interrupt and modify
1045          * HFTRACKCTL with a higher priority depending on user interrupt
1046          * priorities.
1047          */
1048         uintptr_t key = HwiP_disable();
1049 
1050         /* Disable tracking */
1051         HWREG(CKMD_BASE + CKMD_O_HFTRACKCTL) &= ~CKMD_HFTRACKCTL_EN_M;
1052 
1053         /* Re-enable tracking */
1054         HWREG(CKMD_BASE + CKMD_O_HFTRACKCTL) |= CKMD_HFTRACKCTL_EN_M;
1055 
1056         HwiP_restore(key);
1057 
1058         /* Re-enable TRACKREFLOSS */
1059         HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMSET_TRACKREFLOSS_M;
1060     }
1061 
1062     if (maskedStatus & CKMD_MIS_LFCLKGOOD_M)
1063     {
1064         Log_printf(LogModule_Power, Log_INFO, "PowerCC23X0_oscillatorISR: LFCLK is ready");
1065 
1066         /* Enable LF clock monitoring */
1067         HWREG(CKMD_BASE + CKMD_O_LFMONCTL) = CKMD_LFMONCTL_EN;
1068 
1069         /* Enable LF clock loss reset while in standby */
1070         HWREG(PMCTL_BASE + PMCTL_O_RSTCTL) |= PMCTL_RSTCTL_LFLOSS_ARMED;
1071 
1072         /* Send out notification for LF clock switch */
1073         PowerCC23X0_notify(PowerLPF3_LFCLK_SWITCHED);
1074 
1075         /* Allow standby again now that we have sent out the notification */
1076         Power_releaseConstraint(PowerLPF3_DISALLOW_STANDBY);
1077     }
1078 }
1079 
1080 /*
1081  *  ======== PowerCC23X0_startHFXT ========
1082  *  We need to manually start HFXT because the bias voltage around HFXT swings
1083  *  is not stable across voltage or temperature because it is not remeasured
1084  *  every time HFXT starts up.
1085  *  Not manually starting HFXT can lead to:
1086  *  - HFXT never becoming available
1087  *  - Poor phase noise in the radio
1088  *
1089  * It should be ensured that the system does not vector to the CKMD interrupt
1090  * (PowerCC23X0_oscillatorISR) while this function is executing.
1091  * Meaning any of the following must be true:
1092  *  - Interrupts are disabled, or
1093  *  - This function is called from PowerCC23X0_oscillatorISR(), since nested
1094  *    vectoring to the same interrupt is not possible.
1095  */
PowerCC23X0_startHFXT(void)1096 static void PowerCC23X0_startHFXT(void)
1097 {
1098     /* Only start HFXT if it is not already enabled. Trying to start the HFXT
1099      * if it is already enabled will cause TRACKREFLOSS when starting the LDO.
1100      * This situation can arise when:
1101      * - Waking up from fake standby. Fake standby does not shut down the HFXT,
1102      *   unlike real standby.
1103      * - Waking up without actually entering real standby. If a wakeup source is
1104      *   pending when we reach WFI in the ROM function, the hardware will just
1105      *   turn that into a NOP instead and not run through the regular state
1106      *   machine.
1107      */
1108     if ((HWREG(CKMD_BASE + CKMD_O_HFXTCTL) & CKMD_HFXTCTL_EN_M) != CKMD_HFXTCTL_EN)
1109     {
1110         /* Start LDO */
1111         HWREG(CKMD_BASE + CKMD_O_LDOCTL) = (CKMD_LDOCTL_SWOVR | CKMD_LDOCTL_STARTCTL | CKMD_LDOCTL_START |
1112                                             CKMD_LDOCTL_EN);
1113 
1114         /* Bypass a lowpass filter that is connected to the reference voltage
1115          * for 66us to ensure that reference has settled.
1116          */
1117         HapiWaitUs(66);
1118 
1119         /* Clear START bits */
1120         HWREG(CKMD_BASE + CKMD_O_LDOCTL) = (CKMD_LDOCTL_SWOVR | CKMD_LDOCTL_HFXTLVLEN | CKMD_LDOCTL_EN);
1121 
1122         /* Force bias measurement before enabling HFXT - Set SRCSEL = BIAS.
1123          * Enable the peak detector in the HFXT amplitude control loop to
1124          * control RF phase jumps.
1125          */
1126         HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) = (CKMD_AMPADCCTL_SWOVR | CKMD_AMPADCCTL_PEAKDETEN_ENABLE |
1127                                                CKMD_AMPADCCTL_ADCEN_ENABLE);
1128 
1129         /* Delay to settle PEAKDET + ADC */
1130         HapiWaitUs(6);
1131 
1132         /* Clear raw interrupt for ADCBIASUPD */
1133         HWREG(CKMD_BASE + CKMD_O_ICLR) = CKMD_ICLR_ADCBIASUPD;
1134 
1135         /* Start an SAR conversion */
1136         HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) |= CKMD_AMPADCCTL_SARSTRT;
1137 
1138         /* Immediately prevent any SAR new conversions from starting. The one
1139          * started above will complete though.
1140          */
1141         HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) &= ~CKMD_AMPADCCTL_SARSTRT;
1142 
1143         /* Wait until HFXT-ADC BIAS measurement is done */
1144         while (!((HWREG(CKMD_BASE + CKMD_O_RIS) & CKMD_RIS_ADCBIASUPD_M) == CKMD_RIS_ADCBIASUPD)) {}
1145 
1146         /* Clear SW override of amplitude ADC */
1147 
1148         /* Keep PEAKDET on */
1149         HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) &= ~(CKMD_AMPADCCTL_SWOVR_M | CKMD_AMPADCCTL_ADCEN_M);
1150 
1151         /* Start HFXT */
1152         HWREG(CKMD_BASE + CKMD_O_HFXTCTL) |= CKMD_HFXTCTL_EN;
1153     }
1154 
1155     /* Disallow standby until AMPSETTLED is true */
1156     Power_setConstraint(PowerLPF3_DISALLOW_STANDBY);
1157 
1158     /* Enable the AMPSETTLED interrupt.
1159      * Since it is a level status signal, it remains asserted when we are
1160      * running on HFXT and cannot be cleared.
1161      * The oscillator interrupt removes it from the interrupt mask to prevent
1162      * repeated vectoring.
1163      */
1164     HWREG(CKMD_BASE + CKMD_O_ICLR)  = CKMD_ICLR_AMPSETTLED;
1165     HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMSET_AMPSETTLED;
1166 }
1167 
1168 /*
1169  *  ======== PowerCC23X0_notify ========
1170  *  Send notifications to registered clients.
1171  *  Note: Task scheduling is disabled when this function is called.
1172  */
PowerCC23X0_notify(uint_fast16_t eventType)1173 int_fast16_t PowerCC23X0_notify(uint_fast16_t eventType)
1174 {
1175     int_fast16_t notifyStatus;
1176     Power_NotifyFxn notifyFxn;
1177     uintptr_t clientArg;
1178     List_Elem *elem;
1179 
1180     /* If queue is empty, return immediately */
1181     if (!List_empty(&notifyList))
1182     {
1183         /* Point to first client notify object */
1184         elem = List_head(&notifyList);
1185 
1186         /* Walk the queue and notify each registered client of the event */
1187         do
1188         {
1189             if (((Power_NotifyObj *)elem)->eventTypes & eventType)
1190             {
1191                 /* Pull params from notify object */
1192                 notifyFxn = ((Power_NotifyObj *)elem)->notifyFxn;
1193                 clientArg = ((Power_NotifyObj *)elem)->clientArg;
1194 
1195                 /* Call the client's notification function */
1196                 Log_printf(LogModule_Power,
1197                            Log_VERBOSE,
1198                            "PowerCC23X0_notify: Invoking notification fxn at address 0x%x with event type 0x%x and clientArg 0x%x",
1199                            notifyFxn,
1200                            eventType,
1201                            clientArg);
1202 
1203                 notifyStatus = (int_fast16_t)(*(Power_NotifyFxn)notifyFxn)(eventType, 0, clientArg);
1204 
1205                 /* If client declared error stop all further notifications */
1206                 if (notifyStatus != Power_NOTIFYDONE)
1207                 {
1208                     Log_printf(LogModule_Power,
1209                                Log_WARNING,
1210                                "PowerCC23X0_notify: Notification fxn reported error, fxn at address 0x%x with event type 0x%x and notifyStatus 0x%x",
1211                                notifyFxn,
1212                                eventType,
1213                                notifyStatus);
1214 
1215                     return Power_EFAIL;
1216                 }
1217             }
1218 
1219             /* Get next element in the notification queue */
1220             elem = List_next(elem);
1221 
1222         } while (elem != NULL);
1223     }
1224 
1225     return Power_SOK;
1226 }
1227 
1228 /*
1229  *  ======== PowerCC23X0_setDependencyCount ========
1230  */
PowerCC23X0_setDependencyCount(Power_Resource resourceId,uint8_t count)1231 void PowerCC23X0_setDependencyCount(Power_Resource resourceId, uint8_t count)
1232 {
1233     uint8_t bitIndex    = RESOURCE_BIT_INDEX(resourceId);
1234     uint_fast16_t group = RESOURCE_GROUP(resourceId);
1235 
1236     DebugP_assert(PowerCC23X0_isValidResourceId(resourceId));
1237 
1238     if (group == PowerCC23X0_PERIPH_GROUP_CLKCTL0)
1239     {
1240         resourceCountsClkctl0[bitIndex] = count;
1241     }
1242     else if (group == PowerCC23X0_PERIPH_GROUP_LRFD)
1243     {
1244         resourceCountsLrfd[bitIndex] = count;
1245     }
1246 }
1247 
1248 /*
1249  *  ======== PowerCC23X0_isValidResourceId ========
1250  */
PowerCC23X0_isValidResourceId(Power_Resource resourceId)1251 bool PowerCC23X0_isValidResourceId(Power_Resource resourceId)
1252 {
1253     uint8_t bitIndex    = RESOURCE_BIT_INDEX(resourceId);
1254     uint_fast16_t group = RESOURCE_GROUP(resourceId);
1255 
1256     if (group == PowerCC23X0_PERIPH_GROUP_CLKCTL0)
1257     {
1258         return bitIndex < PowerCC23X0_NUMRESOURCES_CLKCTL0;
1259     }
1260     else if (group == PowerCC23X0_PERIPH_GROUP_LRFD)
1261     {
1262         return bitIndex < PowerCC23X0_NUMRESOURCES_LRFD;
1263     }
1264     else
1265     {
1266         return false;
1267     }
1268 }
1269 
1270 /*
1271  *  ======== PowerCC23X0_temperatureToRatio ========
1272  */
PowerCC23X0_temperatureToRatio(int16_t temperature)1273 static uint32_t PowerCC23X0_temperatureToRatio(int16_t temperature)
1274 {
1275     /* Calculate unshifted ppm offset. Fixed-point coefficients are assumed to
1276      * be set so that this computation does not overflow 32 bits in the -40, 125
1277      * degC range.
1278      */
1279     int32_t hfxtPpmOffset = PowerCC23X0_hfxtCompCoefficients.P3 * temperature * temperature * temperature +
1280                             PowerCC23X0_hfxtCompCoefficients.P2 * temperature * temperature +
1281                             PowerCC23X0_hfxtCompCoefficients.P1 * temperature + PowerCC23X0_hfxtCompCoefficients.P0;
1282 
1283     /* Calculate correct frequency offset, using shifted hfxtPpmOffset.
1284      * Frequency offset = 48000000 Hz * (hfxtPpmOffset >> shift) / 1000000
1285      *                  = 48 Hz * hfxtPpmOffset >> shift
1286      * Do 64-bit multiplication, since this will most likely overflow 32 bits.
1287      * Signed right-shift will result in an arithmetic shift operation.
1288      */
1289 #if !(defined(__IAR_SYSTEMS_ICC__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__))
1290     #warning The following signed right-shift operation is implementation-defined
1291 #endif
1292     int32_t hfxtFreqOffset = (int32_t)((48LL * (int64_t)hfxtPpmOffset) >> PowerCC23X0_hfxtCompCoefficients.shift);
1293 
1294     /* Calculate temperature dependent ppm offset of the capacitor array on the
1295      * crystal input pins, modelled as ppm(T) = 0.07 * (T - 25) + 10
1296      * Input frequency is assumed 48 MHz, and any potential crystal offset is
1297      * neglected and not factored into the cap array offset calculation.
1298      * frequency_offset(T) = 48000000 * (0.07 * (T - 25) + 10) / 1000000
1299      *                     = 3.36 * (T - 25) + 480
1300      * To avoid floating-point multiplication and integer division, 3.36 is
1301      * approximated as 3523215 / 2^20 ~ 3.35999966. The error introduced by
1302      * this approximation is negligable.
1303      */
1304 #if !(defined(__IAR_SYSTEMS_ICC__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__))
1305     #warning The following signed right-shift operation is implementation-defined
1306 #endif
1307     int32_t capArrayOffset = ((3523215 * (temperature - 25)) >> 20) + 480;
1308 
1309     /* Calculate the actual reference input frequency to the tracking loop,
1310      * accounting for the HFXT offset and cap array offset
1311      */
1312     int32_t refFreq = 48000000 + hfxtFreqOffset + capArrayOffset;
1313 
1314     /* Calculate word to write to HFTRACKCTL.RATIO. Expression taken from
1315      * register description: ratio = 24MHz / (2 * reference_frequency) * 2^24
1316      * 64-bit division is required, which is truncated to 32 bit
1317      */
1318     uint32_t ratio = (uint32_t)(0xB71B00000000LL / (int64_t)refFreq);
1319 
1320     return ratio;
1321 }
1322 
1323 /*
1324  *  ======== PowerCC23X0_updateHFXTRatio ========
1325  */
PowerCC23X0_updateHFXTRatio(uint32_t ratio)1326 static void PowerCC23X0_updateHFXTRatio(uint32_t ratio)
1327 {
1328     /* Update HFTRACKCTL atomically */
1329     uintptr_t key = HwiP_disable();
1330     uint32_t temp = HWREG(CKMD_BASE + CKMD_O_HFTRACKCTL) & ~CKMD_HFTRACKCTL_RATIO_M;
1331     temp |= ratio & CKMD_HFTRACKCTL_RATIO_M;
1332     HWREG(CKMD_BASE + CKMD_O_HFTRACKCTL) = temp;
1333     HwiP_restore(key);
1334 }
1335 
1336 /*
1337  *  ======== PowerCC23X0_hfxtCompensateFxn ========
1338  */
PowerCC23X0_hfxtCompensateFxn(int16_t currentTemperature,int16_t thresholdTemperature,uintptr_t clientArg,Temperature_NotifyObj * notifyObject)1339 static void PowerCC23X0_hfxtCompensateFxn(int16_t currentTemperature,
1340                                           int16_t thresholdTemperature,
1341                                           uintptr_t clientArg,
1342                                           Temperature_NotifyObj *notifyObject)
1343 {
1344     uintptr_t key;
1345     uint32_t ratio;
1346     PowerCC23X0_hfxtConfig hfxtConfig;
1347     hfxtConfig.value = (uint32_t)clientArg;
1348 
1349     /* Sanitize current temperature to fall within valid range. This ensures
1350      * that 32-bit overflow does not occur in the ppm calculation below.
1351      */
1352     if (currentTemperature > HFXT_COMP_MAX_TEMP)
1353     {
1354         currentTemperature = HFXT_COMP_MAX_TEMP;
1355     }
1356 
1357     if (currentTemperature < HFXT_COMP_MIN_TEMP)
1358     {
1359         currentTemperature = HFXT_COMP_MIN_TEMP;
1360     }
1361 
1362     key = HwiP_disable();
1363 
1364     /* If HFXT compensation has been disabled asynchronously during execution of
1365      * this callback then do not re-register the notification object
1366      */
1367     if (PowerCC23X0_hfxtCompEnabled)
1368     {
1369         Log_printf(LogModule_Power,
1370                    Log_INFO,
1371                    "PowerCC23X0_hfxtCompensateFxn: Registering notification. Temp = %d, Temp threshold = %d",
1372                    currentTemperature,
1373                    hfxtConfig.temperature.threshold);
1374 
1375         if (currentTemperature > hfxtConfig.temperature.threshold)
1376         {
1377             /* If temperature is above compensation threshold then compute a
1378              * compensated ratio-value and update the ratio register in the
1379              * tracking loop
1380              */
1381             ratio = PowerCC23X0_temperatureToRatio(currentTemperature);
1382             PowerCC23X0_updateHFXTRatio(ratio);
1383 
1384             /* Register the notification again with updated thresholds. Notification thresholds must be crossed to
1385              * trigger, so the upper and lower limits are decreased by 1 to maintain a range of +/- delta.
1386              */
1387             Temperature_registerNotifyRange(notifyObject,
1388                                             currentTemperature + hfxtConfig.temperature.delta - 1,
1389                                             currentTemperature - hfxtConfig.temperature.delta + 1,
1390                                             PowerCC23X0_hfxtCompensateFxn,
1391                                             clientArg);
1392         }
1393         else
1394         {
1395             /* If temperature is at or below compensation threshold then reset
1396              * the tracking loop ratio to remove any compensation, and register
1397              * a new high notification. The new limit should not be lower than
1398              * the compensation threshold.
1399              */
1400             PowerCC23X0_updateHFXTRatio(CKMD_HFTRACKCTL_RATIO_REF48M);
1401 
1402             Temperature_registerNotifyHigh(notifyObject,
1403                                            Math_MAX(hfxtConfig.temperature.threshold,
1404                                                     currentTemperature + hfxtConfig.temperature.delta - 1),
1405                                            PowerCC23X0_hfxtCompensateFxn,
1406                                            clientArg);
1407         }
1408     }
1409 
1410     HwiP_restore(key);
1411 }
1412 
1413 /*
1414  *  ======== PowerLPF3_initHFXTCompensation ========
1415  */
PowerLPF3_initHFXTCompensation(int32_t P0,int32_t P1,int32_t P2,int32_t P3,uint8_t shift,bool fcfgInsertion)1416 void PowerLPF3_initHFXTCompensation(int32_t P0,
1417                                     int32_t P1,
1418                                     int32_t P2,
1419                                     int32_t P3,
1420                                     uint8_t shift,
1421                                     __attribute__((unused)) bool fcfgInsertion)
1422 {
1423     PowerCC23X0_hfxtCompCoefficients.P0    = P0;
1424     PowerCC23X0_hfxtCompCoefficients.P1    = P1;
1425     PowerCC23X0_hfxtCompCoefficients.P2    = P2;
1426     PowerCC23X0_hfxtCompCoefficients.P3    = P3;
1427     PowerCC23X0_hfxtCompCoefficients.shift = shift;
1428 
1429     /* If device offers FCFG insertion data it will be factored in here.
1430      * Currently no device supports this.
1431      */
1432 }
1433 
1434 /*
1435  *  ======== PowerLPF3_enableHFXTCompensation ========
1436  */
PowerLPF3_enableHFXTCompensation(int16_t tempThreshold,int16_t tempDelta)1437 void PowerLPF3_enableHFXTCompensation(int16_t tempThreshold, int16_t tempDelta)
1438 {
1439 
1440     if (PowerCC23X0_hfxtCompEnabled == false)
1441     {
1442         PowerCC23X0_hfxtCompEnabled = true;
1443 
1444         Temperature_init();
1445 
1446         int16_t currentTemperature = Temperature_getTemperature();
1447 
1448         PowerCC23X0_hfxtConfig config;
1449         config.temperature.threshold = tempThreshold;
1450         config.temperature.delta     = tempDelta;
1451 
1452         /* Only perform temperature compensation if the temperature is above the
1453          * set threshold. If it is not, then register a high notification on the
1454          * threshold
1455          */
1456         if (currentTemperature > tempThreshold)
1457         {
1458             PowerCC23X0_hfxtCompensateFxn(currentTemperature,
1459                                           0,
1460                                           (uintptr_t)config.value,
1461                                           &PowerCC23X0_hfxtCompNotifyObj);
1462         }
1463         else
1464         {
1465             Temperature_registerNotifyHigh(&PowerCC23X0_hfxtCompNotifyObj,
1466                                            tempThreshold,
1467                                            PowerCC23X0_hfxtCompensateFxn,
1468                                            (uintptr_t)config.value);
1469         }
1470 
1471         Log_printf(LogModule_Power,
1472                    Log_INFO,
1473                    "PowerLPF3_enableHFXTCompensation: Compensation enabled. Temp = %d, Temp threshold = %d",
1474                    currentTemperature,
1475                    tempThreshold);
1476     }
1477 }
1478 
1479 /*
1480  *  ======== PowerLPF3_disableHFXTCompensation ========
1481  */
PowerLPF3_disableHFXTCompensation(void)1482 void PowerLPF3_disableHFXTCompensation(void)
1483 {
1484 
1485     uintptr_t key = HwiP_disable();
1486     if (PowerCC23X0_hfxtCompEnabled == true)
1487     {
1488         PowerCC23X0_hfxtCompEnabled = false;
1489 
1490         Temperature_unregisterNotify(&PowerCC23X0_hfxtCompNotifyObj);
1491 
1492         /* Update HFTRACKCTL.RATIO to reset-value */
1493         PowerCC23X0_updateHFXTRatio(CKMD_HFTRACKCTL_RATIO_REF48M);
1494     }
1495     HwiP_restore(key);
1496 
1497     Log_printf(LogModule_Power,
1498                Log_INFO,
1499                "PowerLPF3_disableHFXTCompensation: Compensation disabled");
1500 }
1501 
1502 /*
1503  *  ======== PowerLPF3_startInitialHfxtAmpComp ========
1504  * Function to start the initial HFXT amplitude compensation.
1505  * If initial HFXT compensation is to be performed, this function is to be
1506  * called in the interrupt handler for the AMPSETTLED interrupt instead of
1507  * posting a PowerLPF3_HFXT_AVAILABLE notification and releasing the
1508  * PowerLPF3_DISALLOW_STANDBY constraint.
1509  *
1510  * The initial HFXT amplitude compensation assumes that the HFXTTARG.IREF value
1511  * is set to the max value (HFXT_TARGET_IREF_MAX) when this function is called.
1512  * The IREF value will then be updated in steps until the optimal IREF
1513  * value has been found. This is the steps taken:
1514  * 1. Wait HFXT_AMP_COMP_MEASUREMENT_US us, to let the HFXT amplitude settle
1515  *    before measuring it.
1516  * 2. Measure the HFXT amplitude.
1517  * 3. If amplitude is above 16, decrement IREF, but never lower than
1518  *    HFXT_TARGET_IREF_MIN.
1519  * 4. If an adjustment was made, go to step 1.
1520  * 5. If amplitude is below 10, increment IREF back to previous value. No new
1521  *    measurement is required.
1522  * 6. If an adjustment was made, wait HFXT_AMP_COMP_MEASUREMENT_US us, to let
1523  *    the HFXT amplitude settle.
1524  * 7. Post PowerLPF3_HFXT_AVAILABLE notification and release
1525  *    PowerLPF3_DISALLOW_STANDBY constraint.
1526  *
1527  * The waits mentioned in step 1 and 6 will be done asynchronously using
1528  * hfxtAmpCompClock, and most of above logic is handled in the callback function
1529  * for the clock (PowerCC23X0_initialHfxtAmpCompClockCb).
1530  *
1531  * This function is only responsible for starting the process by starting the
1532  * clock to perform the first iteration of step 1.
1533  *
1534  * In the worst case (in terms of how long the process takes), the IREF value
1535  * will be decremented to HFXT_TARGET_IREF_MIN and then incremented back to
1536  * (HFXT_TARGET_IREF_MIN + 1). This means that the IREF will be changed
1537  * ((HFXT_TARGET_IREF_MAX - HFXT_TARGET_IREF_MIN) + 1) times. After each change
1538  * we need to wait for HFXT_AMP_COMP_MEASUREMENT_US, and including waiting
1539  * before the first measurement it means that the initial HFXT amplitude
1540  * compensation will take up to
1541  * ((HFXT_TARGET_IREF_MAX - HFXT_TARGET_IREF_MIN) + 2)*HFXT_AMP_COMP_MEASUREMENT_US us
1542  * plus aditional processing time.
1543  */
PowerLPF3_startInitialHfxtAmpComp(void)1544 void PowerLPF3_startInitialHfxtAmpComp(void)
1545 {
1546     /* Update hfxtAmpCompClock callback function */
1547     ClockP_setFunc(&hfxtAmpCompClock, PowerCC23X0_initialHfxtAmpCompClockCb, 0);
1548 
1549     /* Set timeout for the first HFXT amplitude measurement */
1550     ClockP_setTimeout(&hfxtAmpCompClock, HFXT_AMP_COMP_MEASUREMENT_US / ClockP_getSystemTickPeriod());
1551 
1552     /* Start clock */
1553     ClockP_start(&hfxtAmpCompClock);
1554 }
1555 
1556 /*
1557  *  ======== PowerLPF3_getHfxtAmpAdjustment ========
1558  */
PowerLPF3_getHfxtAmpAdjustment(void)1559 int_fast8_t PowerLPF3_getHfxtAmpAdjustment(void)
1560 {
1561     uint32_t hfxtAmplitude   = PowerCC23X0_getHfxtAmpMeasurement();
1562     uint32_t currentIrefTrim = CKMDGetTargetIrefTrim();
1563 
1564     if (hfxtAmplitude < 10 && currentIrefTrim < HFXT_TARGET_IREF_MAX)
1565     {
1566         /* Increase amplitude if the amplitude is too small */
1567         return +1;
1568     }
1569     else if (hfxtAmplitude > 16 && currentIrefTrim > HFXT_TARGET_IREF_MIN)
1570     {
1571         /* Decrease the amplitude if the amplitude is too big */
1572         return -1;
1573     }
1574     else
1575     {
1576         /* No adjustment is needed */
1577         return 0;
1578     }
1579 }
1580 
1581 /*
1582  *  ======== PowerLPF3_adjustHfxtAmp ========
1583  */
PowerLPF3_adjustHfxtAmp(int_fast8_t adjustment)1584 void PowerLPF3_adjustHfxtAmp(int_fast8_t adjustment)
1585 {
1586     uint32_t newTargetIref = CKMDGetTargetIrefTrim() + adjustment;
1587     CKMDSetTargetIrefTrim(newTargetIref);
1588 }
1589 
1590 /*
1591  *  ======== PowerCC23X0_initialHfxtAmpCompClockCb ========
1592  * Callback function for the hfxtAmpCompClock object when it is used for the
1593  * initial HFXT amplitude compensation.
1594  *
1595  * params:
1596  *  - searchDone: If true (non-zero), the search for the optimal IREF value has
1597  *                already been found.
1598  */
PowerCC23X0_initialHfxtAmpCompClockCb(uintptr_t searchDone)1599 static void PowerCC23X0_initialHfxtAmpCompClockCb(uintptr_t searchDone)
1600 {
1601 
1602     /* Has the optimal IREF value been found?
1603      * Might be updated below.
1604      */
1605     bool optimalIref = (bool)searchDone;
1606 
1607     /* Has the IREF value been changed?
1608      * Will be updated below if the IREF value is changed.
1609      */
1610     bool irefChanged = false;
1611 
1612     /* Continue search if the optimal IREF has not yet been found */
1613     if (optimalIref == false)
1614     {
1615         /* Measure amplitude */
1616         uint32_t hfxtAmplitude = PowerCC23X0_getHfxtAmpMeasurement();
1617 
1618         /* Get current IREF value */
1619         uint32_t currentIrefTrim = CKMDGetTargetIrefTrim();
1620 
1621         /* Determine if IREF needs to be changed.
1622          * Note, PowerLPF3_getHfxtAmpAdjustment() is not used here, because the
1623          * amplitude limits used here are different.
1624          */
1625         if (hfxtAmplitude < 10 && currentIrefTrim < HFXT_TARGET_IREF_MAX)
1626         {
1627             /* Increase amplitude if the amplitude is too small */
1628             CKMDSetTargetIrefTrim(currentIrefTrim + 1);
1629             irefChanged = true;
1630 
1631             /* No new measurement is required, because we know
1632              * that IREF = currentIrefTrim + 1 causes an amplitude above 10,
1633              * otherwise IREF would not have been decreased.
1634              */
1635             optimalIref = true;
1636         }
1637         else if (hfxtAmplitude > 16 && currentIrefTrim > HFXT_TARGET_IREF_MIN)
1638         {
1639             /* Decrease amplitude if the amplitude is too big */
1640             CKMDSetTargetIrefTrim(currentIrefTrim - 1);
1641             irefChanged = true;
1642         }
1643         else
1644         {
1645             /* IREF is optimal since since the HFXT amplitude is already in
1646              * the optimal range (10-16)
1647              */
1648             optimalIref = true;
1649         }
1650     }
1651 
1652     if (irefChanged)
1653     {
1654         /* If IREF has changed, start the clock again. If the optimal IREF
1655          * value has been found, then the search is done, so in the next clock
1656          * callback no measurements are needed (searchDone is true).
1657          */
1658         ClockP_setFunc(&hfxtAmpCompClock, PowerCC23X0_initialHfxtAmpCompClockCb, optimalIref);
1659 
1660         /* Set timeout */
1661         ClockP_setTimeout(&hfxtAmpCompClock, HFXT_AMP_COMP_MEASUREMENT_US / ClockP_getSystemTickPeriod());
1662 
1663         /* Start clock */
1664         ClockP_start(&hfxtAmpCompClock);
1665     }
1666     else
1667     {
1668         /* Update hfxtAmpCompClock callback function (NULL because the clock is
1669          * no longer used)
1670          */
1671         ClockP_setFunc(&hfxtAmpCompClock, NULL, 0);
1672 
1673         Log_printf(LogModule_Power, Log_INFO, "PowerCC23X0_initialHfxtAmpCompClockCb: HFXT is available");
1674 
1675         /* Send HFXT available notification */
1676         PowerCC23X0_notify(PowerLPF3_HFXT_AVAILABLE);
1677 
1678         /* Allow standby again now that the optimal IREF value has been found */
1679         Power_releaseConstraint(PowerLPF3_DISALLOW_STANDBY);
1680     }
1681 }
1682 
1683 /*
1684  *  ======== PowerCC23X0_hfxtAmpsettledTimeout ========
1685  * Callback function for the hfxtAmpCompClock object when it is used as a
1686  * timeout for the AMPSETTLED signal. If the timeout occurs (i.e. if this
1687  * function is called), and if the HFXT FSM is stuck in the RAMP0 state,
1688  * it will be forced to the RAMP1 state.
1689  */
PowerCC23X0_hfxtAmpsettledTimeout(uintptr_t arg)1690 static void PowerCC23X0_hfxtAmpsettledTimeout(uintptr_t arg)
1691 {
1692     /* Disable interrupts */
1693     uintptr_t key = HwiP_disable();
1694 
1695     /* Timeout occured while waiting for AMPSETTLED */
1696     if ((HWREG(CKMD_BASE + CKMD_O_AMPSTAT) & CKMD_AMPSTAT_STATE_M) == CKMD_AMPSTAT_STATE_RAMP0)
1697     {
1698         PowerCC23X0_forceHfxtFsmToRamp1();
1699     }
1700 
1701     HwiP_restore(key);
1702 }
1703 
1704 /*
1705  *  ======== PowerCC23X0_forceHfxtFsmToRamp1 ========
1706  * Force HFXT FSM to RAMP1 state. This function should only be called if the
1707  * HFXT FSM is stuck in RAMP0 state.
1708  */
PowerCC23X0_forceHfxtFsmToRamp1(void)1709 static void PowerCC23X0_forceHfxtFsmToRamp1(void)
1710 {
1711     /* Max out IREF */
1712     CKMDSetTargetIrefTrim(HFXT_TARGET_IREF_MAX);
1713 
1714     /* Force a transition to RAMP1 state */
1715     HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) = (CKMD_AMPADCCTL_SWOVR | CKMD_AMPADCCTL_SRCSEL_PEAK |
1716                                            CKMD_AMPADCCTL_ADCEN_ENABLE | CKMD_AMPADCCTL_COMPSTRT);
1717 
1718     /* Wait until state changes */
1719     while ((HWREG(CKMD_BASE + CKMD_O_AMPSTAT) & CKMD_AMPSTAT_STATE_M) == CKMD_AMPSTAT_STATE_RAMP0) {}
1720 
1721     /* Disable AMPADCCTL.SWOVR and let the FSM take control again */
1722     HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) &= ~CKMD_AMPADCCTL_SWOVR;
1723 }
1724 
1725 /*
1726  *  ======== PowerCC23X0_startContHfxtAmpMeasurements ========
1727  * Start continuous measurements of the HFXT amplitude.
1728  * This is done to quickly be able to read the amplitude using
1729  * PowerCC23X0_getHfxtAmpMeasurement() when a measurement is needed.
1730  */
PowerCC23X0_startContHfxtAmpMeasurements(void)1731 static void PowerCC23X0_startContHfxtAmpMeasurements(void)
1732 {
1733     /* Force amplitude measurement - Set SRCSEL = PEAK */
1734     HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) = (CKMD_AMPADCCTL_SWOVR | CKMD_AMPADCCTL_PEAKDETEN_ENABLE |
1735                                            CKMD_AMPADCCTL_ADCEN_ENABLE | CKMD_AMPADCCTL_SRCSEL_PEAK |
1736                                            CKMD_AMPADCCTL_SARSTRT);
1737 }
1738 
1739 /*
1740  *  ======== PowerCC23X0_stopContHfxtAmpMeasurements ========
1741  * Stop the continuous HFXT amplitude measurements started by
1742  * PowerCC23X0_startContHfxtAmpMeasurements().
1743  */
PowerCC23X0_stopContHfxtAmpMeasurements(void)1744 static void PowerCC23X0_stopContHfxtAmpMeasurements(void)
1745 {
1746     /* Clear SW override of Amplitude ADC */
1747     HWREG(CKMD_BASE + CKMD_O_AMPADCCTL) &= ~CKMD_AMPADCCTL_SWOVR;
1748 }
1749 
1750 /*
1751  *  ======== PowerCC23X0_getHfxtAmpMeasurement ========
1752  * Read the the latest HFXT amplitude measurement.
1753  * Continuous measurements must have been started using
1754  * PowerCC23X0_startContHfxtAmpMeasurements().
1755  */
PowerCC23X0_getHfxtAmpMeasurement(void)1756 static uint32_t PowerCC23X0_getHfxtAmpMeasurement(void)
1757 {
1758 
1759     /* Read result in AMPADCSTAT */
1760     uint32_t ampAdcStat = HWREG(CKMD_BASE + CKMD_O_AMPADCSTAT);
1761     uint32_t peakRaw    = (ampAdcStat & CKMD_AMPADCSTAT_PEAKRAW_M) >> CKMD_AMPADCSTAT_PEAKRAW_S;
1762     uint32_t bias       = (ampAdcStat & CKMD_AMPADCSTAT_BIAS_M) >> CKMD_AMPADCSTAT_BIAS_S;
1763 
1764     /* Compute the PEAK value in SW to be able to handle negative values, which
1765      * can occur for small amplitudes.
1766      * In case of negative values, the result will be capped at 0.
1767      * According to the register descriptions PEAK = 2*PEAKRAW - BIAS
1768      */
1769     uint32_t result = 2 * peakRaw > bias ? 2 * peakRaw - bias : 0;
1770 
1771     return result;
1772 }
1773