xref: /rk3399_ARM-atf/drivers/arm/css/scp/css_pm_scpi.c (revision 2d4135e08fb11989a4bbd6ebf9f3c1b324493237)
1*2d4135e0SAntonio Nino Diaz /*
2*2d4135e0SAntonio Nino Diaz  * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
3*2d4135e0SAntonio Nino Diaz  *
4*2d4135e0SAntonio Nino Diaz  * SPDX-License-Identifier: BSD-3-Clause
5*2d4135e0SAntonio Nino Diaz  */
6*2d4135e0SAntonio Nino Diaz 
7*2d4135e0SAntonio Nino Diaz #include <assert.h>
8*2d4135e0SAntonio Nino Diaz 
9*2d4135e0SAntonio Nino Diaz #include <arch_helpers.h>
10*2d4135e0SAntonio Nino Diaz #include <common/debug.h>
11*2d4135e0SAntonio Nino Diaz #include <drivers/arm/css/css_scp.h>
12*2d4135e0SAntonio Nino Diaz #include <drivers/arm/css/css_scpi.h>
13*2d4135e0SAntonio Nino Diaz #include <plat/arm/common/plat_arm.h>
14*2d4135e0SAntonio Nino Diaz #include <plat/arm/css/common/css_pm.h>
15*2d4135e0SAntonio Nino Diaz 
16*2d4135e0SAntonio Nino Diaz /*
17*2d4135e0SAntonio Nino Diaz  * This file implements the SCP power management functions using SCPI protocol.
18*2d4135e0SAntonio Nino Diaz  */
19*2d4135e0SAntonio Nino Diaz 
20*2d4135e0SAntonio Nino Diaz /*
21*2d4135e0SAntonio Nino Diaz  * Helper function to inform power down state to SCP.
22*2d4135e0SAntonio Nino Diaz  */
23*2d4135e0SAntonio Nino Diaz void css_scp_suspend(const struct psci_power_state *target_state)
24*2d4135e0SAntonio Nino Diaz {
25*2d4135e0SAntonio Nino Diaz 	uint32_t cluster_state = scpi_power_on;
26*2d4135e0SAntonio Nino Diaz 	uint32_t system_state = scpi_power_on;
27*2d4135e0SAntonio Nino Diaz 
28*2d4135e0SAntonio Nino Diaz 	/* Check if power down at system power domain level is requested */
29*2d4135e0SAntonio Nino Diaz 	if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF)
30*2d4135e0SAntonio Nino Diaz 		system_state = scpi_power_retention;
31*2d4135e0SAntonio Nino Diaz 
32*2d4135e0SAntonio Nino Diaz 	/* Cluster is to be turned off, so disable coherency */
33*2d4135e0SAntonio Nino Diaz 	if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF)
34*2d4135e0SAntonio Nino Diaz 		cluster_state = scpi_power_off;
35*2d4135e0SAntonio Nino Diaz 
36*2d4135e0SAntonio Nino Diaz 	/*
37*2d4135e0SAntonio Nino Diaz 	 * Ask the SCP to power down the appropriate components depending upon
38*2d4135e0SAntonio Nino Diaz 	 * their state.
39*2d4135e0SAntonio Nino Diaz 	 */
40*2d4135e0SAntonio Nino Diaz 	scpi_set_css_power_state(read_mpidr_el1(),
41*2d4135e0SAntonio Nino Diaz 				 scpi_power_off,
42*2d4135e0SAntonio Nino Diaz 				 cluster_state,
43*2d4135e0SAntonio Nino Diaz 				 system_state);
44*2d4135e0SAntonio Nino Diaz }
45*2d4135e0SAntonio Nino Diaz 
46*2d4135e0SAntonio Nino Diaz /*
47*2d4135e0SAntonio Nino Diaz  * Helper function to turn off a CPU power domain and its parent power domains
48*2d4135e0SAntonio Nino Diaz  * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we
49*2d4135e0SAntonio Nino Diaz  * call the suspend helper here.
50*2d4135e0SAntonio Nino Diaz  */
51*2d4135e0SAntonio Nino Diaz void css_scp_off(const struct psci_power_state *target_state)
52*2d4135e0SAntonio Nino Diaz {
53*2d4135e0SAntonio Nino Diaz 	css_scp_suspend(target_state);
54*2d4135e0SAntonio Nino Diaz }
55*2d4135e0SAntonio Nino Diaz 
56*2d4135e0SAntonio Nino Diaz /*
57*2d4135e0SAntonio Nino Diaz  * Helper function to turn ON a CPU power domain and its parent power domains
58*2d4135e0SAntonio Nino Diaz  * if applicable.
59*2d4135e0SAntonio Nino Diaz  */
60*2d4135e0SAntonio Nino Diaz void css_scp_on(u_register_t mpidr)
61*2d4135e0SAntonio Nino Diaz {
62*2d4135e0SAntonio Nino Diaz 	/*
63*2d4135e0SAntonio Nino Diaz 	 * SCP takes care of powering up parent power domains so we
64*2d4135e0SAntonio Nino Diaz 	 * only need to care about level 0
65*2d4135e0SAntonio Nino Diaz 	 */
66*2d4135e0SAntonio Nino Diaz 	scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on,
67*2d4135e0SAntonio Nino Diaz 				 scpi_power_on);
68*2d4135e0SAntonio Nino Diaz }
69*2d4135e0SAntonio Nino Diaz 
70*2d4135e0SAntonio Nino Diaz /*
71*2d4135e0SAntonio Nino Diaz  * Helper function to get the power state of a power domain node as reported
72*2d4135e0SAntonio Nino Diaz  * by the SCP.
73*2d4135e0SAntonio Nino Diaz  */
74*2d4135e0SAntonio Nino Diaz int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level)
75*2d4135e0SAntonio Nino Diaz {
76*2d4135e0SAntonio Nino Diaz 	int rc, element;
77*2d4135e0SAntonio Nino Diaz 	unsigned int cpu_state, cluster_state;
78*2d4135e0SAntonio Nino Diaz 
79*2d4135e0SAntonio Nino Diaz 	/*
80*2d4135e0SAntonio Nino Diaz 	 * The format of 'power_level' is implementation-defined, but 0 must
81*2d4135e0SAntonio Nino Diaz 	 * mean a CPU. We also allow 1 to denote the cluster
82*2d4135e0SAntonio Nino Diaz 	 */
83*2d4135e0SAntonio Nino Diaz 	if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1)
84*2d4135e0SAntonio Nino Diaz 		return PSCI_E_INVALID_PARAMS;
85*2d4135e0SAntonio Nino Diaz 
86*2d4135e0SAntonio Nino Diaz 	/* Query SCP */
87*2d4135e0SAntonio Nino Diaz 	rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state);
88*2d4135e0SAntonio Nino Diaz 	if (rc != 0)
89*2d4135e0SAntonio Nino Diaz 		return PSCI_E_INVALID_PARAMS;
90*2d4135e0SAntonio Nino Diaz 
91*2d4135e0SAntonio Nino Diaz 	/* Map power states of CPU and cluster to expected PSCI return codes */
92*2d4135e0SAntonio Nino Diaz 	if (power_level == ARM_PWR_LVL0) {
93*2d4135e0SAntonio Nino Diaz 		/*
94*2d4135e0SAntonio Nino Diaz 		 * The CPU state returned by SCP is an 8-bit bit mask
95*2d4135e0SAntonio Nino Diaz 		 * corresponding to each CPU in the cluster
96*2d4135e0SAntonio Nino Diaz 		 */
97*2d4135e0SAntonio Nino Diaz #if ARM_PLAT_MT
98*2d4135e0SAntonio Nino Diaz 		/*
99*2d4135e0SAntonio Nino Diaz 		 * The current SCPI driver only caters for single-threaded
100*2d4135e0SAntonio Nino Diaz 		 * platforms. Hence we ignore the thread ID (which is always 0)
101*2d4135e0SAntonio Nino Diaz 		 * for such platforms.
102*2d4135e0SAntonio Nino Diaz 		 */
103*2d4135e0SAntonio Nino Diaz 		element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK;
104*2d4135e0SAntonio Nino Diaz #else
105*2d4135e0SAntonio Nino Diaz 		element = mpidr & MPIDR_AFFLVL_MASK;
106*2d4135e0SAntonio Nino Diaz #endif  /* ARM_PLAT_MT */
107*2d4135e0SAntonio Nino Diaz 		return CSS_CPU_PWR_STATE(cpu_state, element) ==
108*2d4135e0SAntonio Nino Diaz 			CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF;
109*2d4135e0SAntonio Nino Diaz 	} else {
110*2d4135e0SAntonio Nino Diaz 		assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON ||
111*2d4135e0SAntonio Nino Diaz 				cluster_state == CSS_CLUSTER_PWR_STATE_OFF);
112*2d4135e0SAntonio Nino Diaz 		return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON :
113*2d4135e0SAntonio Nino Diaz 			HW_OFF;
114*2d4135e0SAntonio Nino Diaz 	}
115*2d4135e0SAntonio Nino Diaz }
116*2d4135e0SAntonio Nino Diaz 
117*2d4135e0SAntonio Nino Diaz /*
118*2d4135e0SAntonio Nino Diaz  * Helper function to shutdown the system via SCPI.
119*2d4135e0SAntonio Nino Diaz  */
120*2d4135e0SAntonio Nino Diaz void __dead2 css_scp_sys_shutdown(void)
121*2d4135e0SAntonio Nino Diaz {
122*2d4135e0SAntonio Nino Diaz 	uint32_t response;
123*2d4135e0SAntonio Nino Diaz 
124*2d4135e0SAntonio Nino Diaz 	/*
125*2d4135e0SAntonio Nino Diaz 	 * Disable GIC CPU interface to prevent pending interrupt
126*2d4135e0SAntonio Nino Diaz 	 * from waking up the AP from WFI.
127*2d4135e0SAntonio Nino Diaz 	 */
128*2d4135e0SAntonio Nino Diaz 	plat_arm_gic_cpuif_disable();
129*2d4135e0SAntonio Nino Diaz 
130*2d4135e0SAntonio Nino Diaz 	/* Send the power down request to the SCP */
131*2d4135e0SAntonio Nino Diaz 	response = scpi_sys_power_state(scpi_system_shutdown);
132*2d4135e0SAntonio Nino Diaz 
133*2d4135e0SAntonio Nino Diaz 	if (response != SCP_OK) {
134*2d4135e0SAntonio Nino Diaz 		ERROR("CSS System Off: SCP error %u.\n", response);
135*2d4135e0SAntonio Nino Diaz 		panic();
136*2d4135e0SAntonio Nino Diaz 	}
137*2d4135e0SAntonio Nino Diaz 	wfi();
138*2d4135e0SAntonio Nino Diaz 	ERROR("CSS System Off: operation not handled.\n");
139*2d4135e0SAntonio Nino Diaz 	panic();
140*2d4135e0SAntonio Nino Diaz }
141*2d4135e0SAntonio Nino Diaz 
142*2d4135e0SAntonio Nino Diaz /*
143*2d4135e0SAntonio Nino Diaz  * Helper function to reset the system via SCPI.
144*2d4135e0SAntonio Nino Diaz  */
145*2d4135e0SAntonio Nino Diaz void __dead2 css_scp_sys_reboot(void)
146*2d4135e0SAntonio Nino Diaz {
147*2d4135e0SAntonio Nino Diaz 	uint32_t response;
148*2d4135e0SAntonio Nino Diaz 
149*2d4135e0SAntonio Nino Diaz 	/*
150*2d4135e0SAntonio Nino Diaz 	 * Disable GIC CPU interface to prevent pending interrupt
151*2d4135e0SAntonio Nino Diaz 	 * from waking up the AP from WFI.
152*2d4135e0SAntonio Nino Diaz 	 */
153*2d4135e0SAntonio Nino Diaz 	plat_arm_gic_cpuif_disable();
154*2d4135e0SAntonio Nino Diaz 
155*2d4135e0SAntonio Nino Diaz 	/* Send the system reset request to the SCP */
156*2d4135e0SAntonio Nino Diaz 	response = scpi_sys_power_state(scpi_system_reboot);
157*2d4135e0SAntonio Nino Diaz 
158*2d4135e0SAntonio Nino Diaz 	if (response != SCP_OK) {
159*2d4135e0SAntonio Nino Diaz 		ERROR("CSS System Reset: SCP error %u.\n", response);
160*2d4135e0SAntonio Nino Diaz 		panic();
161*2d4135e0SAntonio Nino Diaz 	}
162*2d4135e0SAntonio Nino Diaz 	wfi();
163*2d4135e0SAntonio Nino Diaz 	ERROR("CSS System Reset: operation not handled.\n");
164*2d4135e0SAntonio Nino Diaz 	panic();
165*2d4135e0SAntonio Nino Diaz }
166