1 /*
2 * Copyright (c) 2019 Linaro Limited.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/kernel.h>
7 #include <zephyr/init.h>
8 #include <zephyr/pm/pm.h>
9 #include <zephyr/pm/policy.h>
10
11 #include <driverlib/pwr_ctrl.h>
12 #include <driverlib/sys_ctrl.h>
13
14 #include <ti/drivers/Power.h>
15 #include <ti/drivers/power/PowerCC26X2.h>
16
17 #include <ti/drivers/dpl/ClockP.h>
18
19 #include <ti/devices/cc13x2x7_cc26x2x7/driverlib/cpu.h>
20 #include <ti/devices/cc13x2x7_cc26x2x7/driverlib/vims.h>
21 #include <ti/devices/cc13x2x7_cc26x2x7/driverlib/sys_ctrl.h>
22
23 #include <zephyr/logging/log.h>
24 #define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
25 LOG_MODULE_REGISTER(soc);
26
27 const PowerCC26X2_Config PowerCC26X2_config = {
28 #if defined(CONFIG_IEEE802154_CC13XX_CC26XX) \
29 || defined(CONFIG_BLE_CC13XX_CC26XX) \
30 || defined(CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ)
31 .policyInitFxn = NULL,
32 .policyFxn = NULL,
33 .calibrateFxn = &PowerCC26XX_calibrate,
34 .enablePolicy = false,
35 .calibrateRCOSC_LF = true,
36 .calibrateRCOSC_HF = true
37 #else
38 /* Configuring TI Power module to not use its policy function (we use Zephyr's
39 * instead), and disable oscillator calibration functionality for now.
40 */
41 .policyInitFxn = NULL,
42 .policyFxn = NULL,
43 .calibrateFxn = NULL,
44 .enablePolicy = false,
45 .calibrateRCOSC_LF = false,
46 .calibrateRCOSC_HF = false
47 #endif
48 };
49
50 extern PowerCC26X2_ModuleState PowerCC26X2_module;
51
52 #ifdef CONFIG_PM
53 /*
54 * Power state mapping:
55 * PM_STATE_SUSPEND_TO_IDLE: Idle
56 * PM_STATE_STANDBY: Standby
57 * PM_STATE_SUSPEND_TO_RAM | PM_STATE_SUSPEND_TO_DISK: Shutdown
58 */
59
60 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)61 void pm_state_set(enum pm_state state, uint8_t substate_id)
62 {
63 ARG_UNUSED(substate_id);
64
65 uint32_t modeVIMS;
66 uint32_t constraints;
67
68 LOG_DBG("SoC entering power state %d", state);
69
70 /* Switch to using PRIMASK instead of BASEPRI register, since
71 * we are only able to wake up from standby while using PRIMASK.
72 */
73 /* Set PRIMASK */
74 CPUcpsid();
75 /* Set BASEPRI to 0 */
76 irq_unlock(0);
77
78 switch (state) {
79 case PM_STATE_SUSPEND_TO_IDLE:
80 /* query the declared constraints */
81 constraints = Power_getConstraintMask();
82 /* 1. Get the current VIMS mode */
83 do {
84 modeVIMS = VIMSModeGet(VIMS_BASE);
85 } while (modeVIMS == VIMS_MODE_CHANGING);
86
87 /* 2. Configure flash to remain on in IDLE or not and keep
88 * VIMS powered on if it is configured as GPRAM
89 * 3. Always keep cache retention ON in IDLE
90 * 4. Turn off the CPU power domain
91 * 5. Ensure any possible outstanding AON writes complete
92 * 6. Enter IDLE
93 */
94 if ((constraints & (1 << PowerCC26XX_NEED_FLASH_IN_IDLE)) ||
95 (modeVIMS == VIMS_MODE_DISABLED)) {
96 SysCtrlIdle(VIMS_ON_BUS_ON_MODE);
97 } else {
98 SysCtrlIdle(VIMS_ON_CPU_ON_MODE);
99 }
100
101 /* 7. Make sure MCU and AON are in sync after wakeup */
102 SysCtrlAonUpdate();
103 break;
104
105 case PM_STATE_STANDBY:
106 /* go to standby mode */
107 Power_sleep(PowerCC26XX_STANDBY);
108 break;
109 case PM_STATE_SUSPEND_TO_RAM:
110 __fallthrough;
111 case PM_STATE_SUSPEND_TO_DISK:
112 __fallthrough;
113 case PM_STATE_SOFT_OFF:
114 Power_shutdown(0, 0);
115 break;
116 default:
117 LOG_DBG("Unsupported power state %u", state);
118 break;
119 }
120
121 LOG_DBG("SoC leaving power state %d", state);
122 }
123
124 /* Handle SOC specific activity after Low Power Mode Exit */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)125 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
126 {
127 ARG_UNUSED(state);
128 ARG_UNUSED(substate_id);
129
130 /*
131 * System is now in active mode. Reenable interrupts which were disabled
132 * when OS started idling code.
133 */
134 CPUcpsie();
135 }
136 #endif /* CONFIG_PM */
137
138 /* Initialize TI Power module */
power_initialize(void)139 static int power_initialize(void)
140 {
141 unsigned int ret;
142
143
144 ret = irq_lock();
145 Power_init();
146 irq_unlock(ret);
147
148 return 0;
149 }
150
151 /*
152 * Unlatch IO pins after waking up from shutdown
153 * This needs to be called during POST_KERNEL in order for "Booting Zephyr"
154 * message to show up
155 */
unlatch_pins(void)156 static int unlatch_pins(void)
157 {
158 /* Get the reason for reset. */
159 uint32_t rSrc = SysCtrlResetSourceGet();
160
161 if (rSrc == RSTSRC_WAKEUP_FROM_SHUTDOWN) {
162 PowerCtrlPadSleepDisable();
163 }
164
165 return 0;
166 }
167
168 /*
169 * ======== PowerCC26XX_schedulerDisable ========
170 */
PowerCC26XX_schedulerDisable(void)171 void PowerCC26XX_schedulerDisable(void)
172 {
173 /*
174 * We are leaving this empty because Zephyr's
175 * scheduler would not get to run with interrupts being disabled
176 * in the context of Power_sleep() in any case.
177 */
178 }
179
180 /*
181 * ======== PowerCC26XX_schedulerRestore ========
182 */
PowerCC26XX_schedulerRestore(void)183 void PowerCC26XX_schedulerRestore(void)
184 {
185 /*
186 * We are leaving this empty because Zephyr's
187 * scheduler would not get to run with interrupts being disabled
188 * in the context of Power_sleep() in any case.
189 */
190 }
191
192 SYS_INIT(power_initialize, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
193 SYS_INIT(unlatch_pins, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
194