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/cc13x2_cc26x2/driverlib/cpu.h>
20 #include <ti/devices/cc13x2_cc26x2/driverlib/vims.h>
21 #include <ti/devices/cc13x2_cc26x2/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 */
58
59 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)60 void pm_state_set(enum pm_state state, uint8_t substate_id)
61 {
62 ARG_UNUSED(substate_id);
63
64 uint32_t modeVIMS;
65 uint32_t constraints;
66
67 LOG_DBG("SoC entering power state %d", state);
68
69 /* Switch to using PRIMASK instead of BASEPRI register, since
70 * we are only able to wake up from standby while using PRIMASK.
71 */
72 /* Set PRIMASK */
73 CPUcpsid();
74 /* Set BASEPRI to 0 */
75 irq_unlock(0);
76
77 switch (state) {
78 case PM_STATE_SUSPEND_TO_IDLE:
79 /* query the declared constraints */
80 constraints = Power_getConstraintMask();
81 /* 1. Get the current VIMS mode */
82 do {
83 modeVIMS = VIMSModeGet(VIMS_BASE);
84 } while (modeVIMS == VIMS_MODE_CHANGING);
85
86 /* 2. Configure flash to remain on in IDLE or not and keep
87 * VIMS powered on if it is configured as GPRAM
88 * 3. Always keep cache retention ON in IDLE
89 * 4. Turn off the CPU power domain
90 * 5. Ensure any possible outstanding AON writes complete
91 * 6. Enter IDLE
92 */
93 if ((constraints & (1 << PowerCC26XX_NEED_FLASH_IN_IDLE)) ||
94 (modeVIMS == VIMS_MODE_DISABLED)) {
95 SysCtrlIdle(VIMS_ON_BUS_ON_MODE);
96 } else {
97 SysCtrlIdle(VIMS_ON_CPU_ON_MODE);
98 }
99
100 /* 7. Make sure MCU and AON are in sync after wakeup */
101 SysCtrlAonUpdate();
102 break;
103
104 case PM_STATE_STANDBY:
105 /* go to standby mode */
106 Power_sleep(PowerCC26XX_STANDBY);
107 break;
108 default:
109 LOG_DBG("Unsupported power state %u", state);
110 break;
111 }
112
113 LOG_DBG("SoC leaving power state %d", state);
114 }
115
116 /* Handle SOC specific activity after Low Power Mode Exit */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)117 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
118 {
119 ARG_UNUSED(state);
120 ARG_UNUSED(substate_id);
121
122 /*
123 * System is now in active mode. Reenable interrupts which were disabled
124 * when OS started idling code.
125 */
126 CPUcpsie();
127 }
128 #endif /* CONFIG_PM */
129
130 /* Initialize TI Power module */
power_initialize(void)131 static int power_initialize(void)
132 {
133 unsigned int ret;
134
135
136 ret = irq_lock();
137 Power_init();
138 irq_unlock(ret);
139
140 return 0;
141 }
142
143 /*
144 * Unlatch IO pins after waking up from shutdown
145 * This needs to be called during POST_KERNEL in order for "Booting Zephyr"
146 * message to show up
147 */
unlatch_pins(void)148 static int unlatch_pins(void)
149 {
150 /* Get the reason for reset. */
151 uint32_t rSrc = SysCtrlResetSourceGet();
152
153 if (rSrc == RSTSRC_WAKEUP_FROM_SHUTDOWN) {
154 PowerCtrlPadSleepDisable();
155 }
156
157 return 0;
158 }
159
160 /*
161 * ======== PowerCC26XX_schedulerDisable ========
162 */
PowerCC26XX_schedulerDisable(void)163 void PowerCC26XX_schedulerDisable(void)
164 {
165 /*
166 * We are leaving this empty because Zephyr's
167 * scheduler would not get to run with interrupts being disabled
168 * in the context of Power_sleep() in any case.
169 */
170 }
171
172 /*
173 * ======== PowerCC26XX_schedulerRestore ========
174 */
PowerCC26XX_schedulerRestore(void)175 void PowerCC26XX_schedulerRestore(void)
176 {
177 /*
178 * We are leaving this empty because Zephyr's
179 * scheduler would not get to run with interrupts being disabled
180 * in the context of Power_sleep() in any case.
181 */
182 }
183
184 SYS_INIT(power_initialize, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
185 SYS_INIT(unlatch_pins, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
186