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(¬ifyList, (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(¬ifyList, (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(¬ifyList))
1182 {
1183 /* Point to first client notify object */
1184 elem = List_head(¬ifyList);
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