blob: 6609530c7d2d875e0c9be5bf0f6a0c5c0dc58e4e [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
29#define DMAC_GLB_REG_SEC 0x694
30#define AXI_CONF_BASE 0x820
31
32static uintptr_t hikey960_sec_entrypoint;
33
34static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state)
35{
36 unsigned long scr;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080037
38 scr = read_scr_el3();
39
Leo Yan3aeba232018-01-04 09:32:50 +080040 /* Enable Physical IRQ and FIQ to wake the CPU */
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080041 write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
42
Leo Yan3aeba232018-01-04 09:32:50 +080043 /* Add barrier before CPU enter WFI state */
44 isb();
45 dsb();
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080046 wfi();
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080047
48 /*
49 * Restore SCR to the original value, synchronisazion of
50 * scr_el3 is done by eret while el3_exit to save some
51 * execution cycles.
52 */
53 write_scr_el3(scr);
54}
55
56static int hikey960_pwr_domain_on(u_register_t mpidr)
57{
58 unsigned int core = mpidr & MPIDR_CPU_MASK;
59 unsigned int cluster =
60 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
61 int cluster_stat = cluster_is_powered_on(cluster);
62
63 hisi_set_cpu_boot_flag(cluster, core);
64
65 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
66 hikey960_sec_entrypoint >> 2);
67
68 if (cluster_stat)
69 hisi_powerup_core(cluster, core);
70 else
71 hisi_powerup_cluster(cluster, core);
72
73 return PSCI_E_SUCCESS;
74}
75
76static void
77hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state)
78{
79 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
80 cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
81
82 gicv2_pcpu_distif_init();
83 gicv2_cpuif_enable();
84}
85
86void hikey960_pwr_domain_off(const psci_power_state_t *target_state)
87{
88 unsigned long mpidr = read_mpidr_el1();
89 unsigned int core = mpidr & MPIDR_CPU_MASK;
90 unsigned int cluster =
91 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
92
93 clr_ex();
94 isb();
95 dsbsy();
96
97 gicv2_cpuif_disable();
98
99 hisi_clear_cpu_boot_flag(cluster, core);
100 hisi_powerdn_core(cluster, core);
101
102 /* check if any core is powered up */
Leo Yanb1f0a372017-06-15 13:51:22 +0800103 if (hisi_test_cpu_down(cluster, core)) {
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800104
105 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
106
107 isb();
108 dsbsy();
109
110 hisi_powerdn_cluster(cluster, core);
111 }
112}
113
114static void __dead2 hikey960_system_reset(void)
115{
116 mmio_write_32(SCTRL_SCPEREN1_REG,
117 SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS);
118 mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef);
119 panic();
120}
121
122int hikey960_validate_power_state(unsigned int power_state,
123 psci_power_state_t *req_state)
124{
Leo Yan6829b702018-01-03 14:52:19 +0800125 unsigned int pstate = psci_get_pstate_type(power_state);
126 unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800127 int i;
128
129 assert(req_state);
130
Leo Yan6829b702018-01-03 14:52:19 +0800131 if (pwr_lvl > PLAT_MAX_PWR_LVL)
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800132 return PSCI_E_INVALID_PARAMS;
133
Leo Yan6829b702018-01-03 14:52:19 +0800134 /* Sanity check the requested state */
135 if (pstate == PSTATE_TYPE_STANDBY) {
136 /*
137 * It's possible to enter standby only on power level 0
138 * Ignore any other power level.
139 */
140 if (pwr_lvl != MPIDR_AFFLVL0)
141 return PSCI_E_INVALID_PARAMS;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800142
Leo Yan6829b702018-01-03 14:52:19 +0800143 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
144 PLAT_MAX_RET_STATE;
145 } else {
146 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
147 req_state->pwr_domain_state[i] =
148 PLAT_MAX_OFF_STATE;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800149 }
150
Leo Yan6829b702018-01-03 14:52:19 +0800151 /*
152 * We expect the 'state id' to be zero.
153 */
154 if (psci_get_pstate_id(power_state))
155 return PSCI_E_INVALID_PARAMS;
156
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800157 return PSCI_E_SUCCESS;
158}
159
160static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint)
161{
162 /*
163 * Check if the non secure entrypoint lies within the non
164 * secure DRAM.
165 */
166 if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE)))
167 return PSCI_E_SUCCESS;
168
169 return PSCI_E_INVALID_ADDRESS;
170}
171
172static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state)
173{
174 u_register_t mpidr = read_mpidr_el1();
175 unsigned int core = mpidr & MPIDR_CPU_MASK;
176 unsigned int cluster =
177 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
178
179 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
180 return;
181
182 if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
183 clr_ex();
184 isb();
185 dsbsy();
186
187 gicv2_cpuif_disable();
188
189 hisi_cpuidle_lock(cluster, core);
190 hisi_set_cpuidle_flag(cluster, core);
191 hisi_cpuidle_unlock(cluster, core);
192
193 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
194 hikey960_sec_entrypoint >> 2);
195
196 hisi_enter_core_idle(cluster, core);
197 }
198
199 /* Perform the common cluster specific operations */
200 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
201 hisi_cpuidle_lock(cluster, core);
202 hisi_disable_pdc(cluster);
203
204 /* check if any core is powered up */
205 if (hisi_test_pwrdn_allcores(cluster, core)) {
206
207 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
208
209 isb();
210 dsbsy();
211
212 /* mask the pdc wakeup irq, then
213 * enable pdc to power down the core
214 */
215 hisi_pdc_mask_cluster_wakeirq(cluster);
216 hisi_enable_pdc(cluster);
217
218 hisi_cpuidle_unlock(cluster, core);
219
220 /* check the SR flag bit to determine
221 * CLUSTER_IDLE_IPC or AP_SR_IPC to send
222 */
223 if (hisi_test_ap_suspend_flag(cluster))
224 hisi_enter_ap_suspend(cluster, core);
225 else
226 hisi_enter_cluster_idle(cluster, core);
227 } else {
228 /* enable pdc */
229 hisi_enable_pdc(cluster);
230 hisi_cpuidle_unlock(cluster, core);
231 }
232 }
233}
234
235static void hikey960_sr_dma_reinit(void)
236{
237 unsigned int ctr = 0;
238
239 mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3);
240
241 /* 1~15 channel is set non_secure */
242 for (ctr = 1; ctr <= 15; ctr++)
243 mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40),
244 (1 << 6) | (1 << 18));
245}
246
247static void
248hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
249{
250 unsigned long mpidr = read_mpidr_el1();
Tao Wang06b9d972017-08-03 17:34:09 +0800251 unsigned int core = mpidr & MPIDR_CPU_MASK;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800252 unsigned int cluster =
253 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
254
255 /* Nothing to be done on waking up from retention from CPU level */
256 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
257 return;
258
Tao Wang06b9d972017-08-03 17:34:09 +0800259 hisi_cpuidle_lock(cluster, core);
260 hisi_clear_cpuidle_flag(cluster, core);
261 hisi_cpuidle_unlock(cluster, core);
262
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800263 if (hisi_test_ap_suspend_flag(cluster)) {
264 hikey960_sr_dma_reinit();
265 gicv2_cpuif_enable();
266 console_init(PL011_UART6_BASE, PL011_UART_CLK_IN_HZ,
267 PL011_BAUDRATE);
268 }
269
270 hikey960_pwr_domain_on_finish(target_state);
271}
272
273static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state)
274{
275 int i;
276
277 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
278 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
279}
280
281static const plat_psci_ops_t hikey960_psci_ops = {
282 .cpu_standby = hikey960_pwr_domain_standby,
283 .pwr_domain_on = hikey960_pwr_domain_on,
284 .pwr_domain_on_finish = hikey960_pwr_domain_on_finish,
285 .pwr_domain_off = hikey960_pwr_domain_off,
286 .pwr_domain_suspend = hikey960_pwr_domain_suspend,
287 .pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish,
288 .system_off = NULL,
289 .system_reset = hikey960_system_reset,
290 .validate_power_state = hikey960_validate_power_state,
291 .validate_ns_entrypoint = hikey960_validate_ns_entrypoint,
292 .get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state,
293};
294
295int plat_setup_psci_ops(uintptr_t sec_entrypoint,
296 const plat_psci_ops_t **psci_ops)
297{
298 hikey960_sec_entrypoint = sec_entrypoint;
299
300 INFO("%s: sec_entrypoint=0x%lx\n", __func__,
301 (unsigned long)hikey960_sec_entrypoint);
302
303 /*
304 * Initialize PSCI ops struct
305 */
306 *psci_ops = &hikey960_psci_ops;
307 return 0;
308}