blob: 078f0d81344f98ef2241276045c210a226231025 [file] [log] [blame]
Haojian Zhuang1b5c2252017-06-01 15:20:46 +08001/*
2 * Copyright (c) 2017, 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 <cci.h>
10#include <console.h>
11#include <debug.h>
12#include <gicv2.h>
13#include <hi3660.h>
14#include <hi3660_crg.h>
15#include <mmio.h>
16#include <psci.h>
17#include "drivers/pwrc/hisi_pwrc.h"
18
19#include "hikey960_def.h"
20#include "hikey960_private.h"
21
22#define CORE_PWR_STATE(state) \
23 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
24#define CLUSTER_PWR_STATE(state) \
25 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
26#define SYSTEM_PWR_STATE(state) \
27 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
28
Leo Yanbc17c4f2017-11-24 14:19:51 +080029#define PSTATE_WIDTH 4
30#define PSTATE_MASK ((1 << PSTATE_WIDTH) - 1)
31
32#define MAKE_PWRSTATE(lvl2_state, lvl1_state, lvl0_state, pwr_lvl, type) \
33 (((lvl2_state) << (PSTATE_ID_SHIFT + PSTATE_WIDTH * 2)) | \
34 ((lvl1_state) << (PSTATE_ID_SHIFT + PSTATE_WIDTH)) | \
35 ((lvl0_state) << (PSTATE_ID_SHIFT)) | \
36 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \
37 ((type) << PSTATE_TYPE_SHIFT))
38
39/*
40 * The table storing the valid idle power states. Ensure that the
41 * array entries are populated in ascending order of state-id to
42 * enable us to use binary search during power state validation.
43 * The table must be terminated by a NULL entry.
44 */
45const unsigned int hikey960_pwr_idle_states[] = {
46 /* State-id - 0x001 */
47 MAKE_PWRSTATE(PLAT_MAX_RUN_STATE, PLAT_MAX_RUN_STATE,
48 PLAT_MAX_STB_STATE, MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
49 /* State-id - 0x002 */
50 MAKE_PWRSTATE(PLAT_MAX_RUN_STATE, PLAT_MAX_RUN_STATE,
51 PLAT_MAX_RET_STATE, MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
52 /* State-id - 0x003 */
53 MAKE_PWRSTATE(PLAT_MAX_RUN_STATE, PLAT_MAX_RUN_STATE,
54 PLAT_MAX_OFF_STATE, MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN),
55 /* State-id - 0x033 */
56 MAKE_PWRSTATE(PLAT_MAX_RUN_STATE, PLAT_MAX_OFF_STATE,
57 PLAT_MAX_OFF_STATE, MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN),
58 0,
59};
60
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080061#define DMAC_GLB_REG_SEC 0x694
62#define AXI_CONF_BASE 0x820
63
64static uintptr_t hikey960_sec_entrypoint;
65
66static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state)
67{
68 unsigned long scr;
69 unsigned int val = 0;
70
Leo Yanbc17c4f2017-11-24 14:19:51 +080071 assert(cpu_state == PLAT_MAX_STB_STATE ||
72 cpu_state == PLAT_MAX_RET_STATE);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080073
74 scr = read_scr_el3();
75
76 /* Enable Physical IRQ and FIQ to wake the CPU*/
77 write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
78
Leo Yanbc17c4f2017-11-24 14:19:51 +080079 if (cpu_state == PLAT_MAX_RET_STATE)
80 set_retention_ticks(val);
81
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080082 wfi();
Leo Yanbc17c4f2017-11-24 14:19:51 +080083
84 if (cpu_state == PLAT_MAX_RET_STATE)
85 clr_retention_ticks(val);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080086
87 /*
88 * Restore SCR to the original value, synchronisazion of
89 * scr_el3 is done by eret while el3_exit to save some
90 * execution cycles.
91 */
92 write_scr_el3(scr);
93}
94
95static int hikey960_pwr_domain_on(u_register_t mpidr)
96{
97 unsigned int core = mpidr & MPIDR_CPU_MASK;
98 unsigned int cluster =
99 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
100 int cluster_stat = cluster_is_powered_on(cluster);
101
102 hisi_set_cpu_boot_flag(cluster, core);
103
104 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
105 hikey960_sec_entrypoint >> 2);
106
107 if (cluster_stat)
108 hisi_powerup_core(cluster, core);
109 else
110 hisi_powerup_cluster(cluster, core);
111
112 return PSCI_E_SUCCESS;
113}
114
115static void
116hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state)
117{
118 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
119 cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
120
121 gicv2_pcpu_distif_init();
122 gicv2_cpuif_enable();
123}
124
125void hikey960_pwr_domain_off(const psci_power_state_t *target_state)
126{
127 unsigned long mpidr = read_mpidr_el1();
128 unsigned int core = mpidr & MPIDR_CPU_MASK;
129 unsigned int cluster =
130 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
131
132 clr_ex();
133 isb();
134 dsbsy();
135
136 gicv2_cpuif_disable();
137
138 hisi_clear_cpu_boot_flag(cluster, core);
139 hisi_powerdn_core(cluster, core);
140
141 /* check if any core is powered up */
Leo Yanb1f0a372017-06-15 13:51:22 +0800142 if (hisi_test_cpu_down(cluster, core)) {
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800143
144 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
145
146 isb();
147 dsbsy();
148
149 hisi_powerdn_cluster(cluster, core);
150 }
151}
152
153static void __dead2 hikey960_system_reset(void)
154{
155 mmio_write_32(SCTRL_SCPEREN1_REG,
156 SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS);
157 mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef);
158 panic();
159}
160
161int hikey960_validate_power_state(unsigned int power_state,
162 psci_power_state_t *req_state)
163{
Leo Yanbc17c4f2017-11-24 14:19:51 +0800164 unsigned int state_id;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800165 int i;
166
167 assert(req_state);
168
Leo Yanbc17c4f2017-11-24 14:19:51 +0800169 /*
170 * Currently we are using a linear search for finding the matching
171 * entry in the idle power state array. This can be made a binary
172 * search if the number of entries justify the additional complexity.
173 */
174 for (i = 0; !!hikey960_pwr_idle_states[i]; i++) {
175 if (power_state == hikey960_pwr_idle_states[i])
176 break;
177 }
178
179 /* Return error if entry not found in the idle state array */
180 if (!hikey960_pwr_idle_states[i])
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800181 return PSCI_E_INVALID_PARAMS;
182
Leo Yanbc17c4f2017-11-24 14:19:51 +0800183 i = 0;
184 state_id = psci_get_pstate_id(power_state);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800185
Leo Yanbc17c4f2017-11-24 14:19:51 +0800186 /* Parse the State ID and populate the state info parameter */
187 while (state_id) {
188 req_state->pwr_domain_state[i++] = state_id & PSTATE_MASK;
189 state_id >>= PSTATE_WIDTH;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800190 }
191
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800192 return PSCI_E_SUCCESS;
193}
194
195static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint)
196{
197 /*
198 * Check if the non secure entrypoint lies within the non
199 * secure DRAM.
200 */
201 if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE)))
202 return PSCI_E_SUCCESS;
203
204 return PSCI_E_INVALID_ADDRESS;
205}
206
207static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state)
208{
209 u_register_t mpidr = read_mpidr_el1();
210 unsigned int core = mpidr & MPIDR_CPU_MASK;
211 unsigned int cluster =
212 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
213
214 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
215 return;
216
217 if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
218 clr_ex();
219 isb();
220 dsbsy();
221
222 gicv2_cpuif_disable();
223
224 hisi_cpuidle_lock(cluster, core);
225 hisi_set_cpuidle_flag(cluster, core);
226 hisi_cpuidle_unlock(cluster, core);
227
228 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
229 hikey960_sec_entrypoint >> 2);
230
231 hisi_enter_core_idle(cluster, core);
232 }
233
234 /* Perform the common cluster specific operations */
235 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
236 hisi_cpuidle_lock(cluster, core);
237 hisi_disable_pdc(cluster);
238
239 /* check if any core is powered up */
240 if (hisi_test_pwrdn_allcores(cluster, core)) {
241
242 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
243
244 isb();
245 dsbsy();
246
247 /* mask the pdc wakeup irq, then
248 * enable pdc to power down the core
249 */
250 hisi_pdc_mask_cluster_wakeirq(cluster);
251 hisi_enable_pdc(cluster);
252
253 hisi_cpuidle_unlock(cluster, core);
254
255 /* check the SR flag bit to determine
256 * CLUSTER_IDLE_IPC or AP_SR_IPC to send
257 */
258 if (hisi_test_ap_suspend_flag(cluster))
259 hisi_enter_ap_suspend(cluster, core);
260 else
261 hisi_enter_cluster_idle(cluster, core);
262 } else {
263 /* enable pdc */
264 hisi_enable_pdc(cluster);
265 hisi_cpuidle_unlock(cluster, core);
266 }
267 }
268}
269
270static void hikey960_sr_dma_reinit(void)
271{
272 unsigned int ctr = 0;
273
274 mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3);
275
276 /* 1~15 channel is set non_secure */
277 for (ctr = 1; ctr <= 15; ctr++)
278 mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40),
279 (1 << 6) | (1 << 18));
280}
281
282static void
283hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
284{
285 unsigned long mpidr = read_mpidr_el1();
Tao Wang06b9d972017-08-03 17:34:09 +0800286 unsigned int core = mpidr & MPIDR_CPU_MASK;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800287 unsigned int cluster =
288 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
289
290 /* Nothing to be done on waking up from retention from CPU level */
291 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
292 return;
293
Tao Wang06b9d972017-08-03 17:34:09 +0800294 hisi_cpuidle_lock(cluster, core);
295 hisi_clear_cpuidle_flag(cluster, core);
296 hisi_cpuidle_unlock(cluster, core);
297
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800298 if (hisi_test_ap_suspend_flag(cluster)) {
299 hikey960_sr_dma_reinit();
300 gicv2_cpuif_enable();
301 console_init(PL011_UART6_BASE, PL011_UART_CLK_IN_HZ,
302 PL011_BAUDRATE);
303 }
304
305 hikey960_pwr_domain_on_finish(target_state);
306}
307
308static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state)
309{
310 int i;
311
312 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
313 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
314}
315
316static const plat_psci_ops_t hikey960_psci_ops = {
317 .cpu_standby = hikey960_pwr_domain_standby,
318 .pwr_domain_on = hikey960_pwr_domain_on,
319 .pwr_domain_on_finish = hikey960_pwr_domain_on_finish,
320 .pwr_domain_off = hikey960_pwr_domain_off,
321 .pwr_domain_suspend = hikey960_pwr_domain_suspend,
322 .pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish,
323 .system_off = NULL,
324 .system_reset = hikey960_system_reset,
325 .validate_power_state = hikey960_validate_power_state,
326 .validate_ns_entrypoint = hikey960_validate_ns_entrypoint,
327 .get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state,
328};
329
330int plat_setup_psci_ops(uintptr_t sec_entrypoint,
331 const plat_psci_ops_t **psci_ops)
332{
333 hikey960_sec_entrypoint = sec_entrypoint;
334
335 INFO("%s: sec_entrypoint=0x%lx\n", __func__,
336 (unsigned long)hikey960_sec_entrypoint);
337
338 /*
339 * Initialize PSCI ops struct
340 */
341 *psci_ops = &hikey960_psci_ops;
342 return 0;
343}