blob: b7f4b90ca13ee3eb9be999fc08e2c2d08958325e [file] [log] [blame]
Varun Wadekarb316e242015-05-19 16:48:04 +05301/*
Marvin Hsu21eea972017-04-11 11:00:48 +08002 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
Varun Wadekarb316e242015-05-19 16:48:04 +05303 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Varun Wadekarb316e242015-05-19 16:48:04 +05305 */
6
Varun Wadekarb316e242015-05-19 16:48:04 +05307#include <assert.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00008#include <arch_helpers.h>
9#include <common/debug.h>
10#include <drivers/delay_timer.h>
11#include <lib/mmio.h>
12#include <lib/psci/psci.h>
13#include <plat/common/platform.h>
14
Varun Wadekara6a357f2017-05-05 09:20:59 -070015#include <bpmp.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000016#include <flowctrl.h>
Varun Wadekarb316e242015-05-19 16:48:04 +053017#include <pmc.h>
Varun Wadekara6a357f2017-05-05 09:20:59 -070018#include <platform_def.h>
19#include <security_engine.h>
Varun Wadekarb316e242015-05-19 16:48:04 +053020#include <tegra_def.h>
21#include <tegra_private.h>
Marvin Hsu21eea972017-04-11 11:00:48 +080022#include <tegra_platform.h>
Varun Wadekarb316e242015-05-19 16:48:04 +053023
Varun Wadekar071b7872015-07-08 17:42:02 +053024/*
25 * Register used to clear CPU reset signals. Each CPU has two reset
26 * signals: CPU reset (3:0) and Core reset (19:16).
27 */
28#define CPU_CMPLX_RESET_CLR 0x454
29#define CPU_CORE_RESET_MASK 0x10001
30
Varun Wadekar8b82fae2015-11-09 17:39:28 -080031/* Clock and Reset controller registers for system clock's settings */
32#define SCLK_RATE 0x30
33#define SCLK_BURST_POLICY 0x28
34#define SCLK_BURST_POLICY_DEFAULT 0x10000000
35
Varun Wadekarb316e242015-05-19 16:48:04 +053036static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
37
Varun Wadekara78bb1b2015-08-07 10:03:00 +053038int32_t tegra_soc_validate_power_state(unsigned int power_state,
39 psci_power_state_t *req_state)
Varun Wadekar254441d2015-07-23 10:07:54 +053040{
Varun Wadekara78bb1b2015-08-07 10:03:00 +053041 int state_id = psci_get_pstate_id(power_state);
42
Varun Wadekar254441d2015-07-23 10:07:54 +053043 /* Sanity check the requested state id */
Varun Wadekara78bb1b2015-08-07 10:03:00 +053044 switch (state_id) {
Varun Wadekar254441d2015-07-23 10:07:54 +053045 case PSTATE_ID_CORE_POWERDN:
Varun Wadekara78bb1b2015-08-07 10:03:00 +053046 /*
47 * Core powerdown request only for afflvl 0
48 */
Varun Wadekara78bb1b2015-08-07 10:03:00 +053049 req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id & 0xff;
50
51 break;
52
Varun Wadekar254441d2015-07-23 10:07:54 +053053 case PSTATE_ID_CLUSTER_IDLE:
54 case PSTATE_ID_CLUSTER_POWERDN:
Varun Wadekara78bb1b2015-08-07 10:03:00 +053055 /*
56 * Cluster powerdown/idle request only for afflvl 1
57 */
Varun Wadekara78bb1b2015-08-07 10:03:00 +053058 req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
Varun Wadekara6a357f2017-05-05 09:20:59 -070059 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PSTATE_ID_CORE_POWERDN;
Varun Wadekara78bb1b2015-08-07 10:03:00 +053060
61 break;
62
Varun Wadekar254441d2015-07-23 10:07:54 +053063 case PSTATE_ID_SOC_POWERDN:
Varun Wadekara78bb1b2015-08-07 10:03:00 +053064 /*
65 * System powerdown request only for afflvl 2
66 */
Varun Wadekar66231d12017-06-07 09:57:42 -070067 for (uint32_t i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
Varun Wadekara78bb1b2015-08-07 10:03:00 +053068 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
69
70 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
71 PLAT_SYS_SUSPEND_STATE_ID;
72
Varun Wadekar254441d2015-07-23 10:07:54 +053073 break;
74
75 default:
Varun Wadekara78bb1b2015-08-07 10:03:00 +053076 ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
77 return PSCI_E_INVALID_PARAMS;
Varun Wadekar254441d2015-07-23 10:07:54 +053078 }
79
80 return PSCI_E_SUCCESS;
81}
82
Varun Wadekarb91b5fc2017-04-18 11:22:01 -070083/*******************************************************************************
84 * Platform handler to calculate the proper target power level at the
85 * specified affinity level
86 ******************************************************************************/
87plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
88 const plat_local_state_t *states,
89 unsigned int ncpu)
90{
Varun Wadekara6a357f2017-05-05 09:20:59 -070091 plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
Varun Wadekarb91b5fc2017-04-18 11:22:01 -070092 int cpu = plat_my_core_pos();
93 int core_pos = read_mpidr() & MPIDR_CPU_MASK;
Varun Wadekara6a357f2017-05-05 09:20:59 -070094 uint32_t bpmp_reply, data[3];
95 int ret;
Varun Wadekarb91b5fc2017-04-18 11:22:01 -070096
97 /* get the power state at this level */
98 if (lvl == MPIDR_AFFLVL1)
99 target = *(states + core_pos);
100 if (lvl == MPIDR_AFFLVL2)
101 target = *(states + cpu);
102
Varun Wadekara6a357f2017-05-05 09:20:59 -0700103 if ((lvl == MPIDR_AFFLVL1) && (target == PSTATE_ID_CLUSTER_IDLE)) {
104
105 /* initialize the bpmp interface */
106 (void)tegra_bpmp_init();
107
108 /* Cluster idle */
109 data[0] = (uint32_t)cpu;
110 data[1] = TEGRA_PM_CC6;
111 data[2] = TEGRA_PM_SC1;
112 ret = tegra_bpmp_send_receive_atomic(MRQ_DO_IDLE,
113 (void *)&data, (int)sizeof(data),
114 (void *)&bpmp_reply, (int)sizeof(bpmp_reply));
Varun Wadekarb91b5fc2017-04-18 11:22:01 -0700115
Varun Wadekara6a357f2017-05-05 09:20:59 -0700116 /* check if cluster idle entry is allowed */
117 if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {
Varun Wadekarb91b5fc2017-04-18 11:22:01 -0700118
Varun Wadekara6a357f2017-05-05 09:20:59 -0700119 /* Cluster idle not allowed */
120 target = PSCI_LOCAL_STATE_RUN;
121 }
122
123 } else if ((lvl == MPIDR_AFFLVL1) && (target == PSTATE_ID_CLUSTER_POWERDN)) {
124
125 /* initialize the bpmp interface */
126 (void)tegra_bpmp_init();
127
128 /* Cluster power-down */
129 data[0] = (uint32_t)cpu;
130 data[1] = TEGRA_PM_CC7;
131 data[2] = TEGRA_PM_SC1;
132 ret = tegra_bpmp_send_receive_atomic(MRQ_DO_IDLE,
133 (void *)&data, (int)sizeof(data),
134 (void *)&bpmp_reply, (int)sizeof(bpmp_reply));
135
136 /* check if cluster power down is allowed */
137 if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {
138
139 /* Cluster power down not allowed */
140 target = PSCI_LOCAL_STATE_RUN;
141 }
142
143 } else if (((lvl == MPIDR_AFFLVL2) || (lvl == MPIDR_AFFLVL1)) &&
144 (target == PSTATE_ID_SOC_POWERDN)) {
145
146 /* System Suspend */
147 target = PSTATE_ID_SOC_POWERDN;
148
149 } else {
150 ; /* do nothing */
151 }
152
153 return target;
Varun Wadekarb91b5fc2017-04-18 11:22:01 -0700154}
155
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530156int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
Varun Wadekarb316e242015-05-19 16:48:04 +0530157{
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530158 u_register_t mpidr = read_mpidr();
159 const plat_local_state_t *pwr_domain_state =
160 target_state->pwr_domain_state;
161 unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
162 unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
163 unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
Marvin Hsu21eea972017-04-11 11:00:48 +0800164 int ret = PSCI_E_SUCCESS;
Varun Wadekarb316e242015-05-19 16:48:04 +0530165
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530166 if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
Varun Wadekarb316e242015-05-19 16:48:04 +0530167
Harvey Hsieh20e9fef2016-12-28 21:53:18 +0800168 assert((stateid_afflvl0 == PLAT_MAX_OFF_STATE) ||
Marvin Hsu21eea972017-04-11 11:00:48 +0800169 (stateid_afflvl0 == PSTATE_ID_SOC_POWERDN));
Harvey Hsieh20e9fef2016-12-28 21:53:18 +0800170 assert((stateid_afflvl1 == PLAT_MAX_OFF_STATE) ||
Marvin Hsu21eea972017-04-11 11:00:48 +0800171 (stateid_afflvl1 == PSTATE_ID_SOC_POWERDN));
172
173 if (tegra_chipid_is_t210_b01()) {
Varun Wadekara6a357f2017-05-05 09:20:59 -0700174
Marvin Hsu21eea972017-04-11 11:00:48 +0800175 /* Suspend se/se2 and pka1 */
176 if (tegra_se_suspend() != 0) {
177 ret = PSCI_E_INTERN_FAIL;
178 }
179
180 /* Save tzram contents */
181 if (tegra_se_save_tzram() != 0) {
182 ret = PSCI_E_INTERN_FAIL;
183 }
184 }
Varun Wadekarb316e242015-05-19 16:48:04 +0530185
Varun Wadekara6a357f2017-05-05 09:20:59 -0700186 /* enter system suspend */
Marvin Hsu21eea972017-04-11 11:00:48 +0800187 if (ret == PSCI_E_SUCCESS) {
188 tegra_fc_soc_powerdn(mpidr);
189 }
Varun Wadekarb316e242015-05-19 16:48:04 +0530190
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530191 } else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_IDLE) {
Varun Wadekarb316e242015-05-19 16:48:04 +0530192
Varun Wadekara6a357f2017-05-05 09:20:59 -0700193 assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
Varun Wadekarb316e242015-05-19 16:48:04 +0530194
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530195 /* Prepare for cluster idle */
196 tegra_fc_cluster_idle(mpidr);
Varun Wadekarb316e242015-05-19 16:48:04 +0530197
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530198 } else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_POWERDN) {
Varun Wadekarb316e242015-05-19 16:48:04 +0530199
Varun Wadekara6a357f2017-05-05 09:20:59 -0700200 assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530201
202 /* Prepare for cluster powerdn */
203 tegra_fc_cluster_powerdn(mpidr);
204
205 } else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {
206
207 /* Prepare for cpu powerdn */
208 tegra_fc_cpu_powerdn(mpidr);
209
210 } else {
Varun Wadekara6a357f2017-05-05 09:20:59 -0700211 ERROR("%s: Unknown state id (%d, %d, %d)\n", __func__,
212 stateid_afflvl2, stateid_afflvl1, stateid_afflvl0);
Marvin Hsu21eea972017-04-11 11:00:48 +0800213 ret = PSCI_E_NOT_SUPPORTED;
Varun Wadekarb316e242015-05-19 16:48:04 +0530214 }
215
Marvin Hsu21eea972017-04-11 11:00:48 +0800216 return ret;
Varun Wadekarb316e242015-05-19 16:48:04 +0530217}
218
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530219int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
Varun Wadekarb316e242015-05-19 16:48:04 +0530220{
Varun Wadekarbc787442015-07-27 13:00:50 +0530221 uint32_t val;
222
Varun Wadekarb316e242015-05-19 16:48:04 +0530223 /*
224 * Check if we are exiting from SOC_POWERDN.
225 */
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530226 if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
227 PLAT_SYS_SUSPEND_STATE_ID) {
Varun Wadekarb316e242015-05-19 16:48:04 +0530228
229 /*
Marvin Hsu21eea972017-04-11 11:00:48 +0800230 * Security engine resume
231 */
232 if (tegra_chipid_is_t210_b01()) {
233 tegra_se_resume();
234 }
235
236 /*
Varun Wadekar6eec6d62016-03-03 13:28:10 -0800237 * Lock scratch registers which hold the CPU vectors
238 */
239 tegra_pmc_lock_cpu_vectors();
240
241 /*
Varun Wadekarbc787442015-07-27 13:00:50 +0530242 * Enable WRAP to INCR burst type conversions for
243 * incoming requests on the AXI slave ports.
244 */
245 val = mmio_read_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG);
246 val &= ~ENABLE_UNSUP_TX_ERRORS;
247 val |= ENABLE_WRAP_TO_INCR_BURSTS;
248 mmio_write_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG, val);
249
250 /*
Varun Wadekarb316e242015-05-19 16:48:04 +0530251 * Restore Boot and Power Management Processor (BPMP) reset
252 * address and reset it.
253 */
254 tegra_fc_reset_bpmp();
Varun Wadekarb316e242015-05-19 16:48:04 +0530255 }
256
257 /*
258 * T210 has a dedicated ARMv7 boot and power mgmt processor, BPMP. It's
259 * used for power management and boot purposes. Inform the BPMP that
260 * we have completed the cluster power up.
261 */
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530262 tegra_fc_lock_active_cluster();
Varun Wadekarb316e242015-05-19 16:48:04 +0530263
264 return PSCI_E_SUCCESS;
265}
266
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530267int tegra_soc_pwr_domain_on(u_register_t mpidr)
Varun Wadekarb316e242015-05-19 16:48:04 +0530268{
269 int cpu = mpidr & MPIDR_CPU_MASK;
Varun Wadekar071b7872015-07-08 17:42:02 +0530270 uint32_t mask = CPU_CORE_RESET_MASK << cpu;
271
272 /* Deassert CPU reset signals */
273 mmio_write_32(TEGRA_CAR_RESET_BASE + CPU_CMPLX_RESET_CLR, mask);
Varun Wadekarb316e242015-05-19 16:48:04 +0530274
275 /* Turn on CPU using flow controller or PMC */
276 if (cpu_powergate_mask[cpu] == 0) {
277 tegra_pmc_cpu_on(cpu);
278 cpu_powergate_mask[cpu] = 1;
279 } else {
280 tegra_fc_cpu_on(cpu);
281 }
282
283 return PSCI_E_SUCCESS;
284}
285
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530286int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
Varun Wadekarb316e242015-05-19 16:48:04 +0530287{
Varun Wadekara78bb1b2015-08-07 10:03:00 +0530288 tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
Varun Wadekarb316e242015-05-19 16:48:04 +0530289 return PSCI_E_SUCCESS;
290}
Varun Wadekar8b82fae2015-11-09 17:39:28 -0800291
292int tegra_soc_prepare_system_reset(void)
293{
294 /*
295 * Set System Clock (SCLK) to POR default so that the clock source
296 * for the PMC APB clock would not be changed due to system reset.
297 */
298 mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_BURST_POLICY,
Marvin Hsu21eea972017-04-11 11:00:48 +0800299 SCLK_BURST_POLICY_DEFAULT);
Varun Wadekar8b82fae2015-11-09 17:39:28 -0800300 mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_RATE, 0);
301
302 /* Wait 1 ms to make sure clock source/device logic is stabilized. */
303 mdelay(1);
304
305 return PSCI_E_SUCCESS;
306}