xref: /rk3399_ARM-atf/plat/allwinner/common/sunxi_scpi_pm.c (revision fe753c97408a92ba245239abd0c056050ae42630)
1*fe753c97SSamuel Holland /*
2*fe753c97SSamuel Holland  * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
3*fe753c97SSamuel Holland  *
4*fe753c97SSamuel Holland  * SPDX-License-Identifier: BSD-3-Clause
5*fe753c97SSamuel Holland  */
6*fe753c97SSamuel Holland 
7*fe753c97SSamuel Holland #include <assert.h>
8*fe753c97SSamuel Holland 
9*fe753c97SSamuel Holland #include <platform_def.h>
10*fe753c97SSamuel Holland 
11*fe753c97SSamuel Holland #include <arch_helpers.h>
12*fe753c97SSamuel Holland #include <common/debug.h>
13*fe753c97SSamuel Holland #include <drivers/arm/css/css_scpi.h>
14*fe753c97SSamuel Holland #include <drivers/arm/gicv2.h>
15*fe753c97SSamuel Holland #include <lib/mmio.h>
16*fe753c97SSamuel Holland #include <lib/psci/psci.h>
17*fe753c97SSamuel Holland 
18*fe753c97SSamuel Holland #include <sunxi_mmap.h>
19*fe753c97SSamuel Holland #include <sunxi_private.h>
20*fe753c97SSamuel Holland 
21*fe753c97SSamuel Holland /*
22*fe753c97SSamuel Holland  * The addresses for the SCP exception vectors are defined in the or1k
23*fe753c97SSamuel Holland  * architecture specification.
24*fe753c97SSamuel Holland  */
25*fe753c97SSamuel Holland #define OR1K_VEC_FIRST			0x01
26*fe753c97SSamuel Holland #define OR1K_VEC_LAST			0x0e
27*fe753c97SSamuel Holland #define OR1K_VEC_ADDR(n)		(0x100 * (n))
28*fe753c97SSamuel Holland 
29*fe753c97SSamuel Holland /*
30*fe753c97SSamuel Holland  * This magic value is the little-endian representation of the or1k
31*fe753c97SSamuel Holland  * instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the
32*fe753c97SSamuel Holland  * first instruction in the SCP firmware.
33*fe753c97SSamuel Holland  */
34*fe753c97SSamuel Holland #define SCP_FIRMWARE_MAGIC		0xb4400012
35*fe753c97SSamuel Holland 
36*fe753c97SSamuel Holland #define CPU_PWR_LVL			MPIDR_AFFLVL0
37*fe753c97SSamuel Holland #define CLUSTER_PWR_LVL			MPIDR_AFFLVL1
38*fe753c97SSamuel Holland #define SYSTEM_PWR_LVL			MPIDR_AFFLVL2
39*fe753c97SSamuel Holland 
40*fe753c97SSamuel Holland #define CPU_PWR_STATE(state) \
41*fe753c97SSamuel Holland 	((state)->pwr_domain_state[CPU_PWR_LVL])
42*fe753c97SSamuel Holland #define CLUSTER_PWR_STATE(state) \
43*fe753c97SSamuel Holland 	((state)->pwr_domain_state[CLUSTER_PWR_LVL])
44*fe753c97SSamuel Holland #define SYSTEM_PWR_STATE(state) \
45*fe753c97SSamuel Holland 	((state)->pwr_domain_state[SYSTEM_PWR_LVL])
46*fe753c97SSamuel Holland 
47*fe753c97SSamuel Holland static inline scpi_power_state_t scpi_map_state(plat_local_state_t psci_state)
48*fe753c97SSamuel Holland {
49*fe753c97SSamuel Holland 	if (is_local_state_run(psci_state)) {
50*fe753c97SSamuel Holland 		return scpi_power_on;
51*fe753c97SSamuel Holland 	}
52*fe753c97SSamuel Holland 	if (is_local_state_retn(psci_state)) {
53*fe753c97SSamuel Holland 		return scpi_power_retention;
54*fe753c97SSamuel Holland 	}
55*fe753c97SSamuel Holland 	return scpi_power_off;
56*fe753c97SSamuel Holland }
57*fe753c97SSamuel Holland 
58*fe753c97SSamuel Holland static void sunxi_cpu_standby(plat_local_state_t cpu_state)
59*fe753c97SSamuel Holland {
60*fe753c97SSamuel Holland 	u_register_t scr = read_scr_el3();
61*fe753c97SSamuel Holland 
62*fe753c97SSamuel Holland 	assert(is_local_state_retn(cpu_state));
63*fe753c97SSamuel Holland 
64*fe753c97SSamuel Holland 	write_scr_el3(scr | SCR_IRQ_BIT);
65*fe753c97SSamuel Holland 	wfi();
66*fe753c97SSamuel Holland 	write_scr_el3(scr);
67*fe753c97SSamuel Holland }
68*fe753c97SSamuel Holland 
69*fe753c97SSamuel Holland static int sunxi_pwr_domain_on(u_register_t mpidr)
70*fe753c97SSamuel Holland {
71*fe753c97SSamuel Holland 	scpi_set_css_power_state(mpidr,
72*fe753c97SSamuel Holland 				 scpi_power_on,
73*fe753c97SSamuel Holland 				 scpi_power_on,
74*fe753c97SSamuel Holland 				 scpi_power_on);
75*fe753c97SSamuel Holland 
76*fe753c97SSamuel Holland 	return PSCI_E_SUCCESS;
77*fe753c97SSamuel Holland }
78*fe753c97SSamuel Holland 
79*fe753c97SSamuel Holland static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
80*fe753c97SSamuel Holland {
81*fe753c97SSamuel Holland 	plat_local_state_t cpu_pwr_state     = CPU_PWR_STATE(target_state);
82*fe753c97SSamuel Holland 	plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state);
83*fe753c97SSamuel Holland 	plat_local_state_t system_pwr_state  = SYSTEM_PWR_STATE(target_state);
84*fe753c97SSamuel Holland 
85*fe753c97SSamuel Holland 	if (is_local_state_off(cpu_pwr_state)) {
86*fe753c97SSamuel Holland 		gicv2_cpuif_disable();
87*fe753c97SSamuel Holland 	}
88*fe753c97SSamuel Holland 
89*fe753c97SSamuel Holland 	scpi_set_css_power_state(read_mpidr(),
90*fe753c97SSamuel Holland 				 scpi_map_state(cpu_pwr_state),
91*fe753c97SSamuel Holland 				 scpi_map_state(cluster_pwr_state),
92*fe753c97SSamuel Holland 				 scpi_map_state(system_pwr_state));
93*fe753c97SSamuel Holland }
94*fe753c97SSamuel Holland 
95*fe753c97SSamuel Holland static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state)
96*fe753c97SSamuel Holland {
97*fe753c97SSamuel Holland 	if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
98*fe753c97SSamuel Holland 		gicv2_distif_init();
99*fe753c97SSamuel Holland 	}
100*fe753c97SSamuel Holland 	if (is_local_state_off(CPU_PWR_STATE(target_state))) {
101*fe753c97SSamuel Holland 		gicv2_pcpu_distif_init();
102*fe753c97SSamuel Holland 		gicv2_cpuif_enable();
103*fe753c97SSamuel Holland 	}
104*fe753c97SSamuel Holland }
105*fe753c97SSamuel Holland 
106*fe753c97SSamuel Holland static void __dead2 sunxi_system_off(void)
107*fe753c97SSamuel Holland {
108*fe753c97SSamuel Holland 	uint32_t ret;
109*fe753c97SSamuel Holland 
110*fe753c97SSamuel Holland 	gicv2_cpuif_disable();
111*fe753c97SSamuel Holland 
112*fe753c97SSamuel Holland 	/* Send the power down request to the SCP. */
113*fe753c97SSamuel Holland 	ret = scpi_sys_power_state(scpi_system_shutdown);
114*fe753c97SSamuel Holland 	if (ret != SCP_OK) {
115*fe753c97SSamuel Holland 		ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret);
116*fe753c97SSamuel Holland 	}
117*fe753c97SSamuel Holland 
118*fe753c97SSamuel Holland 	psci_power_down_wfi();
119*fe753c97SSamuel Holland }
120*fe753c97SSamuel Holland 
121*fe753c97SSamuel Holland static void __dead2 sunxi_system_reset(void)
122*fe753c97SSamuel Holland {
123*fe753c97SSamuel Holland 	uint32_t ret;
124*fe753c97SSamuel Holland 
125*fe753c97SSamuel Holland 	gicv2_cpuif_disable();
126*fe753c97SSamuel Holland 
127*fe753c97SSamuel Holland 	/* Send the system reset request to the SCP. */
128*fe753c97SSamuel Holland 	ret = scpi_sys_power_state(scpi_system_reboot);
129*fe753c97SSamuel Holland 	if (ret != SCP_OK) {
130*fe753c97SSamuel Holland 		ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret);
131*fe753c97SSamuel Holland 	}
132*fe753c97SSamuel Holland 
133*fe753c97SSamuel Holland 	psci_power_down_wfi();
134*fe753c97SSamuel Holland }
135*fe753c97SSamuel Holland 
136*fe753c97SSamuel Holland static int sunxi_validate_power_state(unsigned int power_state,
137*fe753c97SSamuel Holland 				      psci_power_state_t *req_state)
138*fe753c97SSamuel Holland {
139*fe753c97SSamuel Holland 	unsigned int power_level = psci_get_pstate_pwrlvl(power_state);
140*fe753c97SSamuel Holland 	unsigned int type = psci_get_pstate_type(power_state);
141*fe753c97SSamuel Holland 
142*fe753c97SSamuel Holland 	assert(req_state != NULL);
143*fe753c97SSamuel Holland 
144*fe753c97SSamuel Holland 	if (power_level > PLAT_MAX_PWR_LVL) {
145*fe753c97SSamuel Holland 		return PSCI_E_INVALID_PARAMS;
146*fe753c97SSamuel Holland 	}
147*fe753c97SSamuel Holland 
148*fe753c97SSamuel Holland 	if (type == PSTATE_TYPE_STANDBY) {
149*fe753c97SSamuel Holland 		/* Only one retention power state is supported. */
150*fe753c97SSamuel Holland 		if (psci_get_pstate_id(power_state) > 0) {
151*fe753c97SSamuel Holland 			return PSCI_E_INVALID_PARAMS;
152*fe753c97SSamuel Holland 		}
153*fe753c97SSamuel Holland 		/* The SoC cannot be suspended without losing state */
154*fe753c97SSamuel Holland 		if (power_level == SYSTEM_PWR_LVL) {
155*fe753c97SSamuel Holland 			return PSCI_E_INVALID_PARAMS;
156*fe753c97SSamuel Holland 		}
157*fe753c97SSamuel Holland 		for (unsigned int i = 0; i <= power_level; ++i) {
158*fe753c97SSamuel Holland 			req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE;
159*fe753c97SSamuel Holland 		}
160*fe753c97SSamuel Holland 	} else {
161*fe753c97SSamuel Holland 		/* Only one off power state is supported. */
162*fe753c97SSamuel Holland 		if (psci_get_pstate_id(power_state) > 0) {
163*fe753c97SSamuel Holland 			return PSCI_E_INVALID_PARAMS;
164*fe753c97SSamuel Holland 		}
165*fe753c97SSamuel Holland 		for (unsigned int i = 0; i <= power_level; ++i) {
166*fe753c97SSamuel Holland 			req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
167*fe753c97SSamuel Holland 		}
168*fe753c97SSamuel Holland 	}
169*fe753c97SSamuel Holland 	/* Higher power domain levels should all remain running */
170*fe753c97SSamuel Holland 	for (unsigned int i = power_level + 1; i <= PLAT_MAX_PWR_LVL; ++i) {
171*fe753c97SSamuel Holland 		req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN;
172*fe753c97SSamuel Holland 	}
173*fe753c97SSamuel Holland 
174*fe753c97SSamuel Holland 	return PSCI_E_SUCCESS;
175*fe753c97SSamuel Holland }
176*fe753c97SSamuel Holland 
177*fe753c97SSamuel Holland static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state)
178*fe753c97SSamuel Holland {
179*fe753c97SSamuel Holland 	assert(req_state != NULL);
180*fe753c97SSamuel Holland 
181*fe753c97SSamuel Holland 	for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) {
182*fe753c97SSamuel Holland 		req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
183*fe753c97SSamuel Holland 	}
184*fe753c97SSamuel Holland }
185*fe753c97SSamuel Holland 
186*fe753c97SSamuel Holland static const plat_psci_ops_t sunxi_scpi_psci_ops = {
187*fe753c97SSamuel Holland 	.cpu_standby			= sunxi_cpu_standby,
188*fe753c97SSamuel Holland 	.pwr_domain_on			= sunxi_pwr_domain_on,
189*fe753c97SSamuel Holland 	.pwr_domain_off			= sunxi_pwr_domain_off,
190*fe753c97SSamuel Holland 	.pwr_domain_suspend		= sunxi_pwr_domain_off,
191*fe753c97SSamuel Holland 	.pwr_domain_on_finish		= sunxi_pwr_domain_on_finish,
192*fe753c97SSamuel Holland 	.pwr_domain_suspend_finish	= sunxi_pwr_domain_on_finish,
193*fe753c97SSamuel Holland 	.system_off			= sunxi_system_off,
194*fe753c97SSamuel Holland 	.system_reset			= sunxi_system_reset,
195*fe753c97SSamuel Holland 	.validate_power_state		= sunxi_validate_power_state,
196*fe753c97SSamuel Holland 	.validate_ns_entrypoint		= sunxi_validate_ns_entrypoint,
197*fe753c97SSamuel Holland 	.get_sys_suspend_power_state	= sunxi_get_sys_suspend_power_state,
198*fe753c97SSamuel Holland };
199*fe753c97SSamuel Holland 
200*fe753c97SSamuel Holland int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops)
201*fe753c97SSamuel Holland {
202*fe753c97SSamuel Holland 	*psci_ops = &sunxi_scpi_psci_ops;
203*fe753c97SSamuel Holland 
204*fe753c97SSamuel Holland 	/* Check for a valid SCP firmware. */
205*fe753c97SSamuel Holland 	if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) {
206*fe753c97SSamuel Holland 		return -1;
207*fe753c97SSamuel Holland 	}
208*fe753c97SSamuel Holland 
209*fe753c97SSamuel Holland 	/* Program SCP exception vectors to the firmware entrypoint. */
210*fe753c97SSamuel Holland 	for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) {
211*fe753c97SSamuel Holland 		uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i);
212*fe753c97SSamuel Holland 		uint32_t offset = SUNXI_SCP_BASE - vector;
213*fe753c97SSamuel Holland 
214*fe753c97SSamuel Holland 		mmio_write_32(vector, offset >> 2);
215*fe753c97SSamuel Holland 		clean_dcache_range(vector, sizeof(uint32_t));
216*fe753c97SSamuel Holland 	}
217*fe753c97SSamuel Holland 
218*fe753c97SSamuel Holland 	/* Take the SCP out of reset. */
219*fe753c97SSamuel Holland 	mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0));
220*fe753c97SSamuel Holland 
221*fe753c97SSamuel Holland 	/* Wait for the SCP firmware to boot. */
222*fe753c97SSamuel Holland 	return scpi_wait_ready();
223*fe753c97SSamuel Holland }
224