f77746cae1ed320b27312005a3f76759595336ac
[project/bcm63xx/atf.git] / plat / nvidia / tegra / soc / t210 / plat_psci_handlers.c
1 /*
2 * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <debug.h>
10 #include <delay_timer.h>
11 #include <flowctrl.h>
12 #include <mmio.h>
13 #include <platform.h>
14 #include <platform_def.h>
15 #include <pmc.h>
16 #include <psci.h>
17 #include <tegra_def.h>
18 #include <tegra_private.h>
19
20 /*
21 * Register used to clear CPU reset signals. Each CPU has two reset
22 * signals: CPU reset (3:0) and Core reset (19:16).
23 */
24 #define CPU_CMPLX_RESET_CLR 0x454
25 #define CPU_CORE_RESET_MASK 0x10001
26
27 /* Clock and Reset controller registers for system clock's settings */
28 #define SCLK_RATE 0x30
29 #define SCLK_BURST_POLICY 0x28
30 #define SCLK_BURST_POLICY_DEFAULT 0x10000000
31
32 static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
33
34 int32_t tegra_soc_validate_power_state(unsigned int power_state,
35 psci_power_state_t *req_state)
36 {
37 int state_id = psci_get_pstate_id(power_state);
38
39 /* Sanity check the requested state id */
40 switch (state_id) {
41 case PSTATE_ID_CORE_POWERDN:
42 /*
43 * Core powerdown request only for afflvl 0
44 */
45 req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id & 0xff;
46
47 break;
48
49 case PSTATE_ID_CLUSTER_IDLE:
50 case PSTATE_ID_CLUSTER_POWERDN:
51 /*
52 * Cluster powerdown/idle request only for afflvl 1
53 */
54 req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
55 req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id;
56
57 break;
58
59 case PSTATE_ID_SOC_POWERDN:
60 /*
61 * System powerdown request only for afflvl 2
62 */
63 for (uint32_t i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
64 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
65
66 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
67 PLAT_SYS_SUSPEND_STATE_ID;
68
69 break;
70
71 default:
72 ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
73 return PSCI_E_INVALID_PARAMS;
74 }
75
76 return PSCI_E_SUCCESS;
77 }
78
79 /*******************************************************************************
80 * Platform handler to calculate the proper target power level at the
81 * specified affinity level
82 ******************************************************************************/
83 plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
84 const plat_local_state_t *states,
85 unsigned int ncpu)
86 {
87 plat_local_state_t target = *states;
88 int cpu = plat_my_core_pos();
89 int core_pos = read_mpidr() & MPIDR_CPU_MASK;
90
91 /* get the power state at this level */
92 if (lvl == MPIDR_AFFLVL1)
93 target = *(states + core_pos);
94 if (lvl == MPIDR_AFFLVL2)
95 target = *(states + cpu);
96
97 /* Cluster idle/power-down */
98 if ((lvl == MPIDR_AFFLVL1) && ((target == PSTATE_ID_CLUSTER_IDLE) ||
99 (target == PSTATE_ID_CLUSTER_POWERDN))) {
100 return target;
101 }
102
103 /* System Suspend */
104 if (((lvl == MPIDR_AFFLVL2) || (lvl == MPIDR_AFFLVL1)) &&
105 (target == PSTATE_ID_SOC_POWERDN))
106 return PSTATE_ID_SOC_POWERDN;
107
108 /* default state */
109 return PSCI_LOCAL_STATE_RUN;
110 }
111
112 int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
113 {
114 u_register_t mpidr = read_mpidr();
115 const plat_local_state_t *pwr_domain_state =
116 target_state->pwr_domain_state;
117 unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
118 unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
119 unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
120
121 if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
122
123 assert((stateid_afflvl0 == PLAT_MAX_OFF_STATE) ||
124 (stateid_afflvl0 == PSTATE_ID_SOC_POWERDN));
125 assert((stateid_afflvl1 == PLAT_MAX_OFF_STATE) ||
126 (stateid_afflvl1 == PSTATE_ID_SOC_POWERDN));
127
128 /* suspend the entire soc */
129 tegra_fc_soc_powerdn(mpidr);
130
131 } else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_IDLE) {
132
133 assert(stateid_afflvl0 == PSTATE_ID_CLUSTER_IDLE);
134
135 /* Prepare for cluster idle */
136 tegra_fc_cluster_idle(mpidr);
137
138 } else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_POWERDN) {
139
140 assert(stateid_afflvl0 == PSTATE_ID_CLUSTER_POWERDN);
141
142 /* Prepare for cluster powerdn */
143 tegra_fc_cluster_powerdn(mpidr);
144
145 } else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {
146
147 /* Prepare for cpu powerdn */
148 tegra_fc_cpu_powerdn(mpidr);
149
150 } else {
151 ERROR("%s: Unknown state id\n", __func__);
152 return PSCI_E_NOT_SUPPORTED;
153 }
154
155 return PSCI_E_SUCCESS;
156 }
157
158 int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
159 {
160 uint32_t val;
161
162 /*
163 * Check if we are exiting from SOC_POWERDN.
164 */
165 if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
166 PLAT_SYS_SUSPEND_STATE_ID) {
167
168 /*
169 * Lock scratch registers which hold the CPU vectors
170 */
171 tegra_pmc_lock_cpu_vectors();
172
173 /*
174 * Enable WRAP to INCR burst type conversions for
175 * incoming requests on the AXI slave ports.
176 */
177 val = mmio_read_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG);
178 val &= ~ENABLE_UNSUP_TX_ERRORS;
179 val |= ENABLE_WRAP_TO_INCR_BURSTS;
180 mmio_write_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG, val);
181
182 /*
183 * Restore Boot and Power Management Processor (BPMP) reset
184 * address and reset it.
185 */
186 tegra_fc_reset_bpmp();
187 }
188
189 /*
190 * T210 has a dedicated ARMv7 boot and power mgmt processor, BPMP. It's
191 * used for power management and boot purposes. Inform the BPMP that
192 * we have completed the cluster power up.
193 */
194 tegra_fc_lock_active_cluster();
195
196 return PSCI_E_SUCCESS;
197 }
198
199 int tegra_soc_pwr_domain_on(u_register_t mpidr)
200 {
201 int cpu = mpidr & MPIDR_CPU_MASK;
202 uint32_t mask = CPU_CORE_RESET_MASK << cpu;
203
204 /* Deassert CPU reset signals */
205 mmio_write_32(TEGRA_CAR_RESET_BASE + CPU_CMPLX_RESET_CLR, mask);
206
207 /* Turn on CPU using flow controller or PMC */
208 if (cpu_powergate_mask[cpu] == 0) {
209 tegra_pmc_cpu_on(cpu);
210 cpu_powergate_mask[cpu] = 1;
211 } else {
212 tegra_fc_cpu_on(cpu);
213 }
214
215 return PSCI_E_SUCCESS;
216 }
217
218 int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
219 {
220 tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
221 return PSCI_E_SUCCESS;
222 }
223
224 int tegra_soc_prepare_system_reset(void)
225 {
226 /*
227 * Set System Clock (SCLK) to POR default so that the clock source
228 * for the PMC APB clock would not be changed due to system reset.
229 */
230 mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_BURST_POLICY,
231 SCLK_BURST_POLICY_DEFAULT);
232 mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_RATE, 0);
233
234 /* Wait 1 ms to make sure clock source/device logic is stabilized. */
235 mdelay(1);
236
237 return PSCI_E_SUCCESS;
238 }