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