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