blob: 7eb968899eaf7dd1fc3c5d828959b17eb05482ed [file] [log] [blame]
Soby Mathew991d42c2015-06-29 16:30:12 +01001/*
2 * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <arch.h>
32#include <arch_helpers.h>
33#include <assert.h>
34#include <debug.h>
35#include <string.h>
36#include "psci_private.h"
37
38typedef void (*afflvl_off_handler_t)(aff_map_node_t *node);
39
40/*******************************************************************************
41 * The next three functions implement a handler for each supported affinity
42 * level which is called when that affinity level is turned off.
43 ******************************************************************************/
44static void psci_afflvl0_off(aff_map_node_t *cpu_node)
45{
46 assert(cpu_node->level == MPIDR_AFFLVL0);
47
48 /*
49 * Arch. management. Perform the necessary steps to flush all
50 * cpu caches.
51 */
52 psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0);
53
54 /*
55 * Plat. management: Perform platform specific actions to turn this
56 * cpu off e.g. exit cpu coherency, program the power controller etc.
57 */
58 psci_plat_pm_ops->affinst_off(cpu_node->level,
59 psci_get_phys_state(cpu_node));
60}
61
62static void psci_afflvl1_off(aff_map_node_t *cluster_node)
63{
64 /* Sanity check the cluster level */
65 assert(cluster_node->level == MPIDR_AFFLVL1);
66
67 /*
68 * Arch. Management. Flush all levels of caches to PoC if
69 * the cluster is to be shutdown.
70 */
71 psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1);
72
73 /*
74 * Plat. Management. Allow the platform to do its cluster
75 * specific bookeeping e.g. turn off interconnect coherency,
76 * program the power controller etc.
77 */
78 psci_plat_pm_ops->affinst_off(cluster_node->level,
79 psci_get_phys_state(cluster_node));
80}
81
82static void psci_afflvl2_off(aff_map_node_t *system_node)
83{
84 /* Cannot go beyond this level */
85 assert(system_node->level == MPIDR_AFFLVL2);
86
87 /*
88 * Keep the physical state of the system handy to decide what
89 * action needs to be taken
90 */
91
92 /*
93 * Arch. Management. Flush all levels of caches to PoC if
94 * the system is to be shutdown.
95 */
96 psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2);
97
98 /*
99 * Plat. Management : Allow the platform to do its bookeeping
100 * at this affinity level
101 */
102 psci_plat_pm_ops->affinst_off(system_node->level,
103 psci_get_phys_state(system_node));
104}
105
106static const afflvl_off_handler_t psci_afflvl_off_handlers[] = {
107 psci_afflvl0_off,
108 psci_afflvl1_off,
109 psci_afflvl2_off,
110};
111
112/*******************************************************************************
113 * This function takes an array of pointers to affinity instance nodes in the
114 * topology tree and calls the off handler for the corresponding affinity
115 * levels
116 ******************************************************************************/
117static void psci_call_off_handlers(aff_map_node_t *mpidr_nodes[],
118 int start_afflvl,
119 int end_afflvl)
120{
121 int level;
122 aff_map_node_t *node;
123
124 for (level = start_afflvl; level <= end_afflvl; level++) {
125 node = mpidr_nodes[level];
126 if (node == NULL)
127 continue;
128
129 psci_afflvl_off_handlers[level](node);
130 }
131}
132
133/*******************************************************************************
134 * Top level handler which is called when a cpu wants to power itself down.
135 * It's assumed that along with turning the cpu off, higher affinity levels will
136 * be turned off as far as possible. It traverses through all the affinity
137 * levels performing generic, architectural, platform setup and state management
138 * e.g. for a cluster that's to be powered off, it will call the platform
139 * specific code which will disable coherency at the interconnect level if the
140 * cpu is the last in the cluster. For a cpu it could mean programming the power
141 * the power controller etc.
142 *
143 * The state of all the relevant affinity levels is changed prior to calling the
144 * affinity level specific handlers as their actions would depend upon the state
145 * the affinity level is about to enter.
146 *
147 * The affinity level specific handlers are called in ascending order i.e. from
148 * the lowest to the highest affinity level implemented by the platform because
149 * to turn off affinity level X it is neccesary to turn off affinity level X - 1
150 * first.
151 ******************************************************************************/
152int psci_afflvl_off(int start_afflvl,
153 int end_afflvl)
154{
155 int rc;
156 mpidr_aff_map_nodes_t mpidr_nodes;
157 unsigned int max_phys_off_afflvl;
158
159 /*
160 * This function must only be called on platforms where the
161 * CPU_OFF platform hooks have been implemented.
162 */
163 assert(psci_plat_pm_ops->affinst_off);
164
165 /*
166 * Collect the pointers to the nodes in the topology tree for
167 * each affinity instance in the mpidr. If this function does
168 * not return successfully then either the mpidr or the affinity
169 * levels are incorrect. Either way, this an internal TF error
170 * therefore assert.
171 */
172 rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK,
173 start_afflvl,
174 end_afflvl,
175 mpidr_nodes);
176 assert(rc == PSCI_E_SUCCESS);
177
178 /*
179 * This function acquires the lock corresponding to each affinity
180 * level so that by the time all locks are taken, the system topology
181 * is snapshot and state management can be done safely.
182 */
183 psci_acquire_afflvl_locks(start_afflvl,
184 end_afflvl,
185 mpidr_nodes);
186
187
188 /*
189 * Call the cpu off handler registered by the Secure Payload Dispatcher
190 * to let it do any bookkeeping. Assume that the SPD always reports an
191 * E_DENIED error if SP refuse to power down
192 */
193 if (psci_spd_pm && psci_spd_pm->svc_off) {
194 rc = psci_spd_pm->svc_off(0);
195 if (rc)
196 goto exit;
197 }
198
199 /*
200 * This function updates the state of each affinity instance
201 * corresponding to the mpidr in the range of affinity levels
202 * specified.
203 */
204 psci_do_afflvl_state_mgmt(start_afflvl,
205 end_afflvl,
206 mpidr_nodes,
207 PSCI_STATE_OFF);
208
209 max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl,
210 end_afflvl,
211 mpidr_nodes);
212 assert(max_phys_off_afflvl != PSCI_INVALID_DATA);
213
214 /* Stash the highest affinity level that will enter the OFF state. */
215 psci_set_max_phys_off_afflvl(max_phys_off_afflvl);
216
217 /* Perform generic, architecture and platform specific handling */
218 psci_call_off_handlers(mpidr_nodes,
219 start_afflvl,
220 end_afflvl);
221
222 /*
223 * Invalidate the entry for the highest affinity level stashed earlier.
224 * This ensures that any reads of this variable outside the power
225 * up/down sequences return PSCI_INVALID_DATA.
226 *
227 */
228 psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA);
229
230exit:
231 /*
232 * Release the locks corresponding to each affinity level in the
233 * reverse order to which they were acquired.
234 */
235 psci_release_afflvl_locks(start_afflvl,
236 end_afflvl,
237 mpidr_nodes);
238
239 /*
240 * Check if all actions needed to safely power down this cpu have
241 * successfully completed. Enter a wfi loop which will allow the
242 * power controller to physically power down this cpu.
243 */
244 if (rc == PSCI_E_SUCCESS)
245 psci_power_down_wfi();
246
247 return rc;
248}