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