|
| 1 | +/* |
| 2 | + * Copyright (c) 2024 Texas Instruments Incorporated |
| 3 | + * Copyright (c) 2024 Baylibre, SAS |
| 4 | + * |
| 5 | + * SPDX-License-Identifier: Apache-2.0 |
| 6 | + */ |
| 7 | + |
| 8 | +#include <zephyr/kernel.h> |
| 9 | +#include <zephyr/init.h> |
| 10 | +#include <zephyr/pm/pm.h> |
| 11 | +#include <zephyr/pm/policy.h> |
| 12 | + |
| 13 | +#include <ti/drivers/utils/Math.h> |
| 14 | +#include <ti/drivers/Power.h> |
| 15 | +#include <ti/drivers/power/PowerCC23X0.h> |
| 16 | + |
| 17 | +#include <inc/hw_types.h> |
| 18 | +#include <inc/hw_memmap.h> |
| 19 | +#include <inc/hw_ckmd.h> |
| 20 | +#include <inc/hw_systim.h> |
| 21 | +#include <inc/hw_rtc.h> |
| 22 | +#include <inc/hw_evtsvt.h> |
| 23 | +#include <inc/hw_ints.h> |
| 24 | + |
| 25 | +#include <driverlib/lrfd.h> |
| 26 | +#include <driverlib/ull.h> |
| 27 | +#include <driverlib/pmctl.h> |
| 28 | + |
| 29 | +/* Configuring TI Power module to not use its policy function (we use Zephyr's |
| 30 | + * instead), and disable oscillator calibration functionality for now. |
| 31 | + */ |
| 32 | +const PowerCC23X0_Config PowerCC23X0_config = { |
| 33 | + .policyInitFxn = NULL, |
| 34 | + .policyFxn = NULL, |
| 35 | +}; |
| 36 | + |
| 37 | +#ifdef CONFIG_PM |
| 38 | + |
| 39 | +#define MAX_SYSTIMER_DELTA 0xFFBFFFFFU |
| 40 | +#define RTC_TO_SYSTIM_TICKS 8U |
| 41 | +#define SYSTIM_CH_STEP 4U |
| 42 | +#define SYSTIM_CH(idx) (SYSTIM_O_CH0CC + idx * SYSTIM_CH_STEP) |
| 43 | +#define SYSTIM_TO_RTC_SHIFT 3U |
| 44 | +#define SYSTIM_CH_CNT 5U |
| 45 | +#define RTC_NEXT(val, now) (((val - PowerCC23X0_WAKEDELAYSTANDBY) >> SYSTIM_TO_RTC_SHIFT) + now) |
| 46 | + |
| 47 | +static void pm_cc23x0_enter_standby(void); |
| 48 | +static int power_initialize(void); |
| 49 | +extern int_fast16_t PowerCC23X0_notify(uint_fast16_t eventType); |
| 50 | +static void pm_cc23x0_systim_standby_restore(void); |
| 51 | + |
| 52 | +/* Global to stash the SysTimer timeouts while we enter standby */ |
| 53 | +static uint32_t systim[SYSTIM_CH_CNT]; |
| 54 | +static uintptr_t key; |
| 55 | +static uint32_t systim_mask; |
| 56 | + |
| 57 | +/* Shift values to convert between the different resolutions of the SysTimer |
| 58 | + * channels. Channel 0 can technically support either 1us or 250ns. Until the |
| 59 | + * channel is actively used, we will hard-code it to 1us resolution to improve |
| 60 | + * runtime. |
| 61 | + */ |
| 62 | +const uint8_t systim_offset[SYSTIM_CH_CNT] = { |
| 63 | + 0, /* 1us */ |
| 64 | + 0, /* 1us */ |
| 65 | + 2, /* 250ns -> 1us */ |
| 66 | + 2, /* 250ns -> 1us */ |
| 67 | + 2 /* 250ns -> 1us */ |
| 68 | +}; |
| 69 | + |
| 70 | +static void pm_cc23x0_systim_standby_restore(void) |
| 71 | +{ |
| 72 | + HWREG(RTC_BASE + RTC_O_ARMCLR) = RTC_ARMCLR_CH0_CLR; |
| 73 | + HWREG(RTC_BASE + RTC_O_ICLR) = RTC_ICLR_EV0_CLR; |
| 74 | + |
| 75 | + ULLSync(); |
| 76 | + |
| 77 | + HwiP_clearInterrupt(INT_CPUIRQ16); |
| 78 | + HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ16SEL) = EVTSVT_CPUIRQ16SEL_PUBID_SYSTIM0; |
| 79 | + |
| 80 | + while (HWREG(SYSTIM_BASE + SYSTIM_O_STATUS) != SYSTIM_STATUS_VAL_RUN) { |
| 81 | + ; |
| 82 | + } |
| 83 | + |
| 84 | + for (uint8_t idx = 0; idx < SYSTIM_CH_CNT; idx++) { |
| 85 | + if (systim_mask & (1 << idx)) { |
| 86 | + HWREG(SYSTIM_BASE + SYSTIM_CH(idx)) = systim[idx]; |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + HWREG(SYSTIM_BASE + SYSTIM_O_IMASK) = systim_mask; |
| 91 | + LRFDApplyClockDependencies(); |
| 92 | + PowerCC23X0_notify(PowerLPF3_AWAKE_STANDBY); |
| 93 | + |
| 94 | + HwiP_restore(key); |
| 95 | +} |
| 96 | + |
| 97 | +static void pm_cc23x0_enter_standby(void) |
| 98 | +{ |
| 99 | + uint32_t rtc_now = 0; |
| 100 | + uint32_t systim_now = 0; |
| 101 | + uint32_t systim_next = MAX_SYSTIMER_DELTA; |
| 102 | + uint32_t systim_delta = 0; |
| 103 | + |
| 104 | + key = HwiP_disable(); |
| 105 | + |
| 106 | + uint32_t constraints = Power_getConstraintMask(); |
| 107 | + bool standby = (constraints & (1 << PowerLPF3_DISALLOW_STANDBY)) == 0; |
| 108 | + bool idle = (constraints & (1 << PowerLPF3_DISALLOW_IDLE)) == 0; |
| 109 | + |
| 110 | + if (standby && (HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) & CKMD_LFCLKSEL_MAIN_LFOSC) && |
| 111 | + !(HWREG(CKMD_BASE + CKMD_O_LFCLKSTAT) & CKMD_LFCLKSTAT_FLTSETTLED_M)) { |
| 112 | + standby = false; |
| 113 | + idle = false; |
| 114 | + } |
| 115 | + |
| 116 | + if (standby) { |
| 117 | + systim_mask = HWREG(SYSTIM_BASE + SYSTIM_O_IMASK); |
| 118 | + if (systim_mask != 0) { |
| 119 | + systim_next = 0xFFFFFFFF; |
| 120 | + systim_now = HWREG(SYSTIM_BASE + SYSTIM_O_TIME1U); |
| 121 | + for (uint8_t idx = 0; idx < SYSTIM_CH_CNT; idx++) { |
| 122 | + if (systim_mask & (1 << idx)) { |
| 123 | + systim[idx] = HWREG(SYSTIM_BASE + SYSTIM_CH(idx)); |
| 124 | + systim_delta = systim[idx]; |
| 125 | + systim_delta -= systim_now << systim_offset[idx]; |
| 126 | + |
| 127 | + if (systim_delta > MAX_SYSTIMER_DELTA) { |
| 128 | + systim_delta = 0; |
| 129 | + } |
| 130 | + |
| 131 | + systim_delta = systim_delta >> systim_offset[idx]; |
| 132 | + systim_next = MIN(systim_next, systim_delta); |
| 133 | + } |
| 134 | + } |
| 135 | + } else { |
| 136 | + systim_next = MAX_SYSTIMER_DELTA; |
| 137 | + } |
| 138 | + |
| 139 | + if (systim_next > PowerCC23X0_TOTALTIMESTANDBY) { |
| 140 | + HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ16SEL) = |
| 141 | + EVTSVT_CPUIRQ16SEL_PUBID_AON_RTC_COMB; |
| 142 | + HwiP_clearInterrupt(INT_CPUIRQ16); |
| 143 | + rtc_now = HWREG(RTC_BASE + RTC_O_TIME8U); |
| 144 | + HWREG(RTC_BASE + RTC_O_CH0CC8U) = RTC_NEXT(systim_next, rtc_now); |
| 145 | + |
| 146 | + Power_sleep(PowerLPF3_STANDBY); |
| 147 | + pm_cc23x0_systim_standby_restore(); |
| 148 | + } else if (idle) { |
| 149 | + __WFI(); |
| 150 | + } |
| 151 | + } else if (idle) { |
| 152 | + __WFI(); |
| 153 | + } |
| 154 | + |
| 155 | + HwiP_restore(key); |
| 156 | +} |
| 157 | + |
| 158 | +void pm_state_set(enum pm_state state, uint8_t substate_id) |
| 159 | +{ |
| 160 | + ARG_UNUSED(substate_id); |
| 161 | + |
| 162 | + switch (state) { |
| 163 | + case PM_STATE_RUNTIME_IDLE: |
| 164 | + PowerCC23X0_doWFI(); |
| 165 | + break; |
| 166 | + case PM_STATE_STANDBY: |
| 167 | + pm_cc23x0_enter_standby(); |
| 168 | + break; |
| 169 | + case PM_STATE_SOFT_OFF: |
| 170 | + Power_shutdown(0, 0); |
| 171 | + break; |
| 172 | + default: |
| 173 | + break; |
| 174 | + } |
| 175 | +} |
| 176 | + |
| 177 | +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) |
| 178 | +{ |
| 179 | + ARG_UNUSED(state); |
| 180 | + ARG_UNUSED(substate_id); |
| 181 | + |
| 182 | + HwiP_restore(0); |
| 183 | +} |
| 184 | + |
| 185 | +#endif /* CONFIG_PM */ |
| 186 | + |
| 187 | +static int power_initialize(void) |
| 188 | +{ |
| 189 | + Power_init(); |
| 190 | + |
| 191 | + if (DT_HAS_COMPAT_STATUS_OKAY(ti_cc23x0_lf_xosc)) { |
| 192 | + PowerLPF3_selectLFXT(); |
| 193 | + } |
| 194 | + |
| 195 | + PMCTLSetVoltageRegulator(PMCTL_VOLTAGE_REGULATOR_DCDC); |
| 196 | + |
| 197 | + return 0; |
| 198 | +} |
| 199 | + |
| 200 | +SYS_INIT(power_initialize, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |
0 commit comments