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