blob: f836508d5e4ec56d3e2c0cc4920ca057147aa9c2 [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
Haojian Zhuang1b5c2252017-06-01 15:20:46 +08007#include <assert.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00008
9#include <arch_helpers.h>
10#include <common/debug.h>
11#include <drivers/arm/cci.h>
12#include <drivers/arm/gicv2.h>
13#include <drivers/arm/pl011.h>
Leo Yan3886dc62020-03-02 22:15:08 +080014#include <drivers/arm/pl061_gpio.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000015#include <drivers/delay_timer.h>
16#include <lib/mmio.h>
17#include <lib/psci/psci.h>
18
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080019#include <hi3660.h>
20#include <hi3660_crg.h>
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080021
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000022#include "drivers/pwrc/hisi_pwrc.h"
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080023#include "hikey960_def.h"
24#include "hikey960_private.h"
25
26#define CORE_PWR_STATE(state) \
27 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
28#define CLUSTER_PWR_STATE(state) \
29 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
30#define SYSTEM_PWR_STATE(state) \
31 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
32
33#define DMAC_GLB_REG_SEC 0x694
34#define AXI_CONF_BASE 0x820
35
Haojian Zhuang6a009492018-02-17 12:06:18 +080036static unsigned int uart_base;
Andre Przywara2b1b1a52020-01-25 00:58:35 +000037static console_t console;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080038static uintptr_t hikey960_sec_entrypoint;
39
40static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state)
41{
42 unsigned long scr;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080043
44 scr = read_scr_el3();
45
Leo Yan3aeba232018-01-04 09:32:50 +080046 /* Enable Physical IRQ and FIQ to wake the CPU */
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080047 write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
48
Leo Yan3aeba232018-01-04 09:32:50 +080049 /* Add barrier before CPU enter WFI state */
50 isb();
51 dsb();
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080052 wfi();
Haojian Zhuang1b5c2252017-06-01 15:20:46 +080053
54 /*
55 * Restore SCR to the original value, synchronisazion of
56 * scr_el3 is done by eret while el3_exit to save some
57 * execution cycles.
58 */
59 write_scr_el3(scr);
60}
61
62static int hikey960_pwr_domain_on(u_register_t mpidr)
63{
64 unsigned int core = mpidr & MPIDR_CPU_MASK;
65 unsigned int cluster =
66 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
67 int cluster_stat = cluster_is_powered_on(cluster);
68
69 hisi_set_cpu_boot_flag(cluster, core);
70
71 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
72 hikey960_sec_entrypoint >> 2);
73
74 if (cluster_stat)
75 hisi_powerup_core(cluster, core);
76 else
77 hisi_powerup_cluster(cluster, core);
78
79 return PSCI_E_SUCCESS;
80}
81
82static void
83hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state)
84{
85 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
86 cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
87
88 gicv2_pcpu_distif_init();
89 gicv2_cpuif_enable();
90}
91
92void hikey960_pwr_domain_off(const psci_power_state_t *target_state)
93{
94 unsigned long mpidr = read_mpidr_el1();
95 unsigned int core = mpidr & MPIDR_CPU_MASK;
96 unsigned int cluster =
97 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
98
99 clr_ex();
100 isb();
101 dsbsy();
102
103 gicv2_cpuif_disable();
104
105 hisi_clear_cpu_boot_flag(cluster, core);
106 hisi_powerdn_core(cluster, core);
107
108 /* check if any core is powered up */
Leo Yanb1f0a372017-06-15 13:51:22 +0800109 if (hisi_test_cpu_down(cluster, core)) {
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800110
111 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
112
113 isb();
114 dsbsy();
115
116 hisi_powerdn_cluster(cluster, core);
117 }
118}
119
Leo Yan3886dc62020-03-02 22:15:08 +0800120static void __dead2 hikey960_system_off(void)
121{
122 gpio_set_direction(176, GPIO_DIR_OUT);
123 gpio_set_value(176, GPIO_LEVEL_LOW);
124 panic();
125}
126
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800127static void __dead2 hikey960_system_reset(void)
128{
Haojian Zhuang01650282018-03-26 13:18:13 +0800129 dsb();
130 isb();
131 mdelay(2000);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800132 mmio_write_32(SCTRL_SCPEREN1_REG,
133 SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS);
134 mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef);
135 panic();
136}
137
138int hikey960_validate_power_state(unsigned int power_state,
139 psci_power_state_t *req_state)
140{
Leo Yan6829b702018-01-03 14:52:19 +0800141 unsigned int pstate = psci_get_pstate_type(power_state);
142 unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800143 int i;
144
145 assert(req_state);
146
Leo Yan6829b702018-01-03 14:52:19 +0800147 if (pwr_lvl > PLAT_MAX_PWR_LVL)
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800148 return PSCI_E_INVALID_PARAMS;
149
Leo Yan6829b702018-01-03 14:52:19 +0800150 /* Sanity check the requested state */
151 if (pstate == PSTATE_TYPE_STANDBY) {
152 /*
153 * It's possible to enter standby only on power level 0
154 * Ignore any other power level.
155 */
156 if (pwr_lvl != MPIDR_AFFLVL0)
157 return PSCI_E_INVALID_PARAMS;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800158
Leo Yan6829b702018-01-03 14:52:19 +0800159 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
160 PLAT_MAX_RET_STATE;
161 } else {
162 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
163 req_state->pwr_domain_state[i] =
164 PLAT_MAX_OFF_STATE;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800165 }
166
Leo Yan6829b702018-01-03 14:52:19 +0800167 /*
168 * We expect the 'state id' to be zero.
169 */
170 if (psci_get_pstate_id(power_state))
171 return PSCI_E_INVALID_PARAMS;
172
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800173 return PSCI_E_SUCCESS;
174}
175
176static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint)
177{
178 /*
179 * Check if the non secure entrypoint lies within the non
180 * secure DRAM.
181 */
182 if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE)))
183 return PSCI_E_SUCCESS;
184
185 return PSCI_E_INVALID_ADDRESS;
186}
187
188static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state)
189{
190 u_register_t mpidr = read_mpidr_el1();
191 unsigned int core = mpidr & MPIDR_CPU_MASK;
192 unsigned int cluster =
193 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
194
195 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
196 return;
197
198 if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
199 clr_ex();
200 isb();
201 dsbsy();
202
203 gicv2_cpuif_disable();
204
205 hisi_cpuidle_lock(cluster, core);
206 hisi_set_cpuidle_flag(cluster, core);
207 hisi_cpuidle_unlock(cluster, core);
208
209 mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
210 hikey960_sec_entrypoint >> 2);
211
212 hisi_enter_core_idle(cluster, core);
213 }
214
215 /* Perform the common cluster specific operations */
216 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
217 hisi_cpuidle_lock(cluster, core);
218 hisi_disable_pdc(cluster);
219
220 /* check if any core is powered up */
221 if (hisi_test_pwrdn_allcores(cluster, core)) {
222
223 cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
224
225 isb();
226 dsbsy();
227
228 /* mask the pdc wakeup irq, then
229 * enable pdc to power down the core
230 */
231 hisi_pdc_mask_cluster_wakeirq(cluster);
232 hisi_enable_pdc(cluster);
233
234 hisi_cpuidle_unlock(cluster, core);
235
236 /* check the SR flag bit to determine
237 * CLUSTER_IDLE_IPC or AP_SR_IPC to send
238 */
Wei Yu633fc062019-04-07 11:29:28 +0800239 if (hisi_test_ap_suspend_flag())
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800240 hisi_enter_ap_suspend(cluster, core);
241 else
242 hisi_enter_cluster_idle(cluster, core);
243 } else {
244 /* enable pdc */
245 hisi_enable_pdc(cluster);
246 hisi_cpuidle_unlock(cluster, core);
247 }
248 }
249}
250
251static void hikey960_sr_dma_reinit(void)
252{
253 unsigned int ctr = 0;
254
255 mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3);
256
257 /* 1~15 channel is set non_secure */
258 for (ctr = 1; ctr <= 15; ctr++)
259 mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40),
260 (1 << 6) | (1 << 18));
261}
262
263static void
264hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
265{
266 unsigned long mpidr = read_mpidr_el1();
Tao Wang06b9d972017-08-03 17:34:09 +0800267 unsigned int core = mpidr & MPIDR_CPU_MASK;
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800268 unsigned int cluster =
269 (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
270
271 /* Nothing to be done on waking up from retention from CPU level */
272 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
273 return;
274
Tao Wang06b9d972017-08-03 17:34:09 +0800275 hisi_cpuidle_lock(cluster, core);
276 hisi_clear_cpuidle_flag(cluster, core);
277 hisi_cpuidle_unlock(cluster, core);
278
Wei Yu633fc062019-04-07 11:29:28 +0800279 if (hisi_test_ap_suspend_flag()) {
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800280 hikey960_sr_dma_reinit();
281 gicv2_cpuif_enable();
Jerome Forissier3fb19df2018-11-08 09:59:29 +0100282 console_pl011_register(uart_base, PL011_UART_CLK_IN_HZ,
283 PL011_BAUDRATE, &console);
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800284 }
285
286 hikey960_pwr_domain_on_finish(target_state);
287}
288
289static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state)
290{
291 int i;
292
293 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
294 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
295}
296
297static const plat_psci_ops_t hikey960_psci_ops = {
298 .cpu_standby = hikey960_pwr_domain_standby,
299 .pwr_domain_on = hikey960_pwr_domain_on,
300 .pwr_domain_on_finish = hikey960_pwr_domain_on_finish,
301 .pwr_domain_off = hikey960_pwr_domain_off,
302 .pwr_domain_suspend = hikey960_pwr_domain_suspend,
303 .pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish,
Leo Yan3886dc62020-03-02 22:15:08 +0800304 .system_off = hikey960_system_off,
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800305 .system_reset = hikey960_system_reset,
306 .validate_power_state = hikey960_validate_power_state,
307 .validate_ns_entrypoint = hikey960_validate_ns_entrypoint,
308 .get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state,
309};
310
311int plat_setup_psci_ops(uintptr_t sec_entrypoint,
312 const plat_psci_ops_t **psci_ops)
313{
Haojian Zhuang6a009492018-02-17 12:06:18 +0800314 unsigned int id = 0;
315 int ret;
316
317 ret = hikey960_read_boardid(&id);
318 if (ret == 0) {
319 if (id == 5300U)
320 uart_base = PL011_UART5_BASE;
321 else
322 uart_base = PL011_UART6_BASE;
323 } else {
324 uart_base = PL011_UART6_BASE;
325 }
326
Haojian Zhuang1b5c2252017-06-01 15:20:46 +0800327 hikey960_sec_entrypoint = sec_entrypoint;
328
329 INFO("%s: sec_entrypoint=0x%lx\n", __func__,
330 (unsigned long)hikey960_sec_entrypoint);
331
332 /*
333 * Initialize PSCI ops struct
334 */
335 *psci_ops = &hikey960_psci_ops;
336 return 0;
337}