1*532ed618SSoby Mathew /* 2*532ed618SSoby Mathew * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 3*532ed618SSoby Mathew * 4*532ed618SSoby Mathew * Redistribution and use in source and binary forms, with or without 5*532ed618SSoby Mathew * modification, are permitted provided that the following conditions are met: 6*532ed618SSoby Mathew * 7*532ed618SSoby Mathew * Redistributions of source code must retain the above copyright notice, this 8*532ed618SSoby Mathew * list of conditions and the following disclaimer. 9*532ed618SSoby Mathew * 10*532ed618SSoby Mathew * Redistributions in binary form must reproduce the above copyright notice, 11*532ed618SSoby Mathew * this list of conditions and the following disclaimer in the documentation 12*532ed618SSoby Mathew * and/or other materials provided with the distribution. 13*532ed618SSoby Mathew * 14*532ed618SSoby Mathew * Neither the name of ARM nor the names of its contributors may be used 15*532ed618SSoby Mathew * to endorse or promote products derived from this software without specific 16*532ed618SSoby Mathew * prior written permission. 17*532ed618SSoby Mathew * 18*532ed618SSoby Mathew * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19*532ed618SSoby Mathew * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*532ed618SSoby Mathew * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*532ed618SSoby Mathew * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22*532ed618SSoby Mathew * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23*532ed618SSoby Mathew * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24*532ed618SSoby Mathew * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25*532ed618SSoby Mathew * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26*532ed618SSoby Mathew * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27*532ed618SSoby Mathew * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28*532ed618SSoby Mathew * POSSIBILITY OF SUCH DAMAGE. 29*532ed618SSoby Mathew */ 30*532ed618SSoby Mathew 31*532ed618SSoby Mathew #include <arch.h> 32*532ed618SSoby Mathew #include <arch_helpers.h> 33*532ed618SSoby Mathew #include <assert.h> 34*532ed618SSoby Mathew #include <bl_common.h> 35*532ed618SSoby Mathew #include <context.h> 36*532ed618SSoby Mathew #include <context_mgmt.h> 37*532ed618SSoby Mathew #include <platform.h> 38*532ed618SSoby Mathew #include <stddef.h> 39*532ed618SSoby Mathew #include "psci_private.h" 40*532ed618SSoby Mathew 41*532ed618SSoby Mathew /******************************************************************************* 42*532ed618SSoby Mathew * Per cpu non-secure contexts used to program the architectural state prior 43*532ed618SSoby Mathew * return to the normal world. 44*532ed618SSoby Mathew * TODO: Use the memory allocator to set aside memory for the contexts instead 45*532ed618SSoby Mathew * of relying on platform defined constants. 46*532ed618SSoby Mathew ******************************************************************************/ 47*532ed618SSoby Mathew static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; 48*532ed618SSoby Mathew 49*532ed618SSoby Mathew /****************************************************************************** 50*532ed618SSoby Mathew * Define the psci capability variable. 51*532ed618SSoby Mathew *****************************************************************************/ 52*532ed618SSoby Mathew unsigned int psci_caps; 53*532ed618SSoby Mathew 54*532ed618SSoby Mathew /******************************************************************************* 55*532ed618SSoby Mathew * Function which initializes the 'psci_non_cpu_pd_nodes' or the 56*532ed618SSoby Mathew * 'psci_cpu_pd_nodes' corresponding to the power level. 57*532ed618SSoby Mathew ******************************************************************************/ 58*532ed618SSoby Mathew static void psci_init_pwr_domain_node(unsigned int node_idx, 59*532ed618SSoby Mathew unsigned int parent_idx, 60*532ed618SSoby Mathew unsigned int level) 61*532ed618SSoby Mathew { 62*532ed618SSoby Mathew if (level > PSCI_CPU_PWR_LVL) { 63*532ed618SSoby Mathew psci_non_cpu_pd_nodes[node_idx].level = level; 64*532ed618SSoby Mathew psci_lock_init(psci_non_cpu_pd_nodes, node_idx); 65*532ed618SSoby Mathew psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx; 66*532ed618SSoby Mathew psci_non_cpu_pd_nodes[node_idx].local_state = 67*532ed618SSoby Mathew PLAT_MAX_OFF_STATE; 68*532ed618SSoby Mathew } else { 69*532ed618SSoby Mathew psci_cpu_data_t *svc_cpu_data; 70*532ed618SSoby Mathew 71*532ed618SSoby Mathew psci_cpu_pd_nodes[node_idx].parent_node = parent_idx; 72*532ed618SSoby Mathew 73*532ed618SSoby Mathew /* Initialize with an invalid mpidr */ 74*532ed618SSoby Mathew psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR; 75*532ed618SSoby Mathew 76*532ed618SSoby Mathew svc_cpu_data = 77*532ed618SSoby Mathew &(_cpu_data_by_index(node_idx)->psci_svc_cpu_data); 78*532ed618SSoby Mathew 79*532ed618SSoby Mathew /* Set the Affinity Info for the cores as OFF */ 80*532ed618SSoby Mathew svc_cpu_data->aff_info_state = AFF_STATE_OFF; 81*532ed618SSoby Mathew 82*532ed618SSoby Mathew /* Invalidate the suspend level for the cpu */ 83*532ed618SSoby Mathew svc_cpu_data->target_pwrlvl = PSCI_INVALID_PWR_LVL; 84*532ed618SSoby Mathew 85*532ed618SSoby Mathew /* Set the power state to OFF state */ 86*532ed618SSoby Mathew svc_cpu_data->local_state = PLAT_MAX_OFF_STATE; 87*532ed618SSoby Mathew 88*532ed618SSoby Mathew flush_dcache_range((uintptr_t)svc_cpu_data, 89*532ed618SSoby Mathew sizeof(*svc_cpu_data)); 90*532ed618SSoby Mathew 91*532ed618SSoby Mathew cm_set_context_by_index(node_idx, 92*532ed618SSoby Mathew (void *) &psci_ns_context[node_idx], 93*532ed618SSoby Mathew NON_SECURE); 94*532ed618SSoby Mathew } 95*532ed618SSoby Mathew } 96*532ed618SSoby Mathew 97*532ed618SSoby Mathew /******************************************************************************* 98*532ed618SSoby Mathew * This functions updates cpu_start_idx and ncpus field for each of the node in 99*532ed618SSoby Mathew * psci_non_cpu_pd_nodes[]. It does so by comparing the parent nodes of each of 100*532ed618SSoby Mathew * the CPUs and check whether they match with the parent of the previous 101*532ed618SSoby Mathew * CPU. The basic assumption for this work is that children of the same parent 102*532ed618SSoby Mathew * are allocated adjacent indices. The platform should ensure this though proper 103*532ed618SSoby Mathew * mapping of the CPUs to indices via plat_core_pos_by_mpidr() and 104*532ed618SSoby Mathew * plat_my_core_pos() APIs. 105*532ed618SSoby Mathew *******************************************************************************/ 106*532ed618SSoby Mathew static void psci_update_pwrlvl_limits(void) 107*532ed618SSoby Mathew { 108*532ed618SSoby Mathew int j; 109*532ed618SSoby Mathew unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0}; 110*532ed618SSoby Mathew unsigned int temp_index[PLAT_MAX_PWR_LVL], cpu_idx; 111*532ed618SSoby Mathew 112*532ed618SSoby Mathew for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { 113*532ed618SSoby Mathew psci_get_parent_pwr_domain_nodes(cpu_idx, 114*532ed618SSoby Mathew PLAT_MAX_PWR_LVL, 115*532ed618SSoby Mathew temp_index); 116*532ed618SSoby Mathew for (j = PLAT_MAX_PWR_LVL - 1; j >= 0; j--) { 117*532ed618SSoby Mathew if (temp_index[j] != nodes_idx[j]) { 118*532ed618SSoby Mathew nodes_idx[j] = temp_index[j]; 119*532ed618SSoby Mathew psci_non_cpu_pd_nodes[nodes_idx[j]].cpu_start_idx 120*532ed618SSoby Mathew = cpu_idx; 121*532ed618SSoby Mathew } 122*532ed618SSoby Mathew psci_non_cpu_pd_nodes[nodes_idx[j]].ncpus++; 123*532ed618SSoby Mathew } 124*532ed618SSoby Mathew } 125*532ed618SSoby Mathew } 126*532ed618SSoby Mathew 127*532ed618SSoby Mathew /******************************************************************************* 128*532ed618SSoby Mathew * Core routine to populate the power domain tree. The tree descriptor passed by 129*532ed618SSoby Mathew * the platform is populated breadth-first and the first entry in the map 130*532ed618SSoby Mathew * informs the number of root power domains. The parent nodes of the root nodes 131*532ed618SSoby Mathew * will point to an invalid entry(-1). 132*532ed618SSoby Mathew ******************************************************************************/ 133*532ed618SSoby Mathew static void populate_power_domain_tree(const unsigned char *topology) 134*532ed618SSoby Mathew { 135*532ed618SSoby Mathew unsigned int i, j = 0, num_nodes_at_lvl = 1, num_nodes_at_next_lvl; 136*532ed618SSoby Mathew unsigned int node_index = 0, parent_node_index = 0, num_children; 137*532ed618SSoby Mathew int level = PLAT_MAX_PWR_LVL; 138*532ed618SSoby Mathew 139*532ed618SSoby Mathew /* 140*532ed618SSoby Mathew * For each level the inputs are: 141*532ed618SSoby Mathew * - number of nodes at this level in plat_array i.e. num_nodes_at_level 142*532ed618SSoby Mathew * This is the sum of values of nodes at the parent level. 143*532ed618SSoby Mathew * - Index of first entry at this level in the plat_array i.e. 144*532ed618SSoby Mathew * parent_node_index. 145*532ed618SSoby Mathew * - Index of first free entry in psci_non_cpu_pd_nodes[] or 146*532ed618SSoby Mathew * psci_cpu_pd_nodes[] i.e. node_index depending upon the level. 147*532ed618SSoby Mathew */ 148*532ed618SSoby Mathew while (level >= PSCI_CPU_PWR_LVL) { 149*532ed618SSoby Mathew num_nodes_at_next_lvl = 0; 150*532ed618SSoby Mathew /* 151*532ed618SSoby Mathew * For each entry (parent node) at this level in the plat_array: 152*532ed618SSoby Mathew * - Find the number of children 153*532ed618SSoby Mathew * - Allocate a node in a power domain array for each child 154*532ed618SSoby Mathew * - Set the parent of the child to the parent_node_index - 1 155*532ed618SSoby Mathew * - Increment parent_node_index to point to the next parent 156*532ed618SSoby Mathew * - Accumulate the number of children at next level. 157*532ed618SSoby Mathew */ 158*532ed618SSoby Mathew for (i = 0; i < num_nodes_at_lvl; i++) { 159*532ed618SSoby Mathew assert(parent_node_index <= 160*532ed618SSoby Mathew PSCI_NUM_NON_CPU_PWR_DOMAINS); 161*532ed618SSoby Mathew num_children = topology[parent_node_index]; 162*532ed618SSoby Mathew 163*532ed618SSoby Mathew for (j = node_index; 164*532ed618SSoby Mathew j < node_index + num_children; j++) 165*532ed618SSoby Mathew psci_init_pwr_domain_node(j, 166*532ed618SSoby Mathew parent_node_index - 1, 167*532ed618SSoby Mathew level); 168*532ed618SSoby Mathew 169*532ed618SSoby Mathew node_index = j; 170*532ed618SSoby Mathew num_nodes_at_next_lvl += num_children; 171*532ed618SSoby Mathew parent_node_index++; 172*532ed618SSoby Mathew } 173*532ed618SSoby Mathew 174*532ed618SSoby Mathew num_nodes_at_lvl = num_nodes_at_next_lvl; 175*532ed618SSoby Mathew level--; 176*532ed618SSoby Mathew 177*532ed618SSoby Mathew /* Reset the index for the cpu power domain array */ 178*532ed618SSoby Mathew if (level == PSCI_CPU_PWR_LVL) 179*532ed618SSoby Mathew node_index = 0; 180*532ed618SSoby Mathew } 181*532ed618SSoby Mathew 182*532ed618SSoby Mathew /* Validate the sanity of array exported by the platform */ 183*532ed618SSoby Mathew assert(j == PLATFORM_CORE_COUNT); 184*532ed618SSoby Mathew } 185*532ed618SSoby Mathew 186*532ed618SSoby Mathew /******************************************************************************* 187*532ed618SSoby Mathew * This function initializes the power domain topology tree by querying the 188*532ed618SSoby Mathew * platform. The power domain nodes higher than the CPU are populated in the 189*532ed618SSoby Mathew * array psci_non_cpu_pd_nodes[] and the CPU power domains are populated in 190*532ed618SSoby Mathew * psci_cpu_pd_nodes[]. The platform exports its static topology map through the 191*532ed618SSoby Mathew * populate_power_domain_topology_tree() API. The algorithm populates the 192*532ed618SSoby Mathew * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this 193*532ed618SSoby Mathew * topology map. On a platform that implements two clusters of 2 cpus each, and 194*532ed618SSoby Mathew * supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would look 195*532ed618SSoby Mathew * like this: 196*532ed618SSoby Mathew * 197*532ed618SSoby Mathew * --------------------------------------------------- 198*532ed618SSoby Mathew * | system node | cluster 0 node | cluster 1 node | 199*532ed618SSoby Mathew * --------------------------------------------------- 200*532ed618SSoby Mathew * 201*532ed618SSoby Mathew * And populated psci_cpu_pd_nodes would look like this : 202*532ed618SSoby Mathew * <- cpus cluster0 -><- cpus cluster1 -> 203*532ed618SSoby Mathew * ------------------------------------------------ 204*532ed618SSoby Mathew * | CPU 0 | CPU 1 | CPU 2 | CPU 3 | 205*532ed618SSoby Mathew * ------------------------------------------------ 206*532ed618SSoby Mathew ******************************************************************************/ 207*532ed618SSoby Mathew int psci_setup(void) 208*532ed618SSoby Mathew { 209*532ed618SSoby Mathew const unsigned char *topology_tree; 210*532ed618SSoby Mathew 211*532ed618SSoby Mathew /* Query the topology map from the platform */ 212*532ed618SSoby Mathew topology_tree = plat_get_power_domain_tree_desc(); 213*532ed618SSoby Mathew 214*532ed618SSoby Mathew /* Populate the power domain arrays using the platform topology map */ 215*532ed618SSoby Mathew populate_power_domain_tree(topology_tree); 216*532ed618SSoby Mathew 217*532ed618SSoby Mathew /* Update the CPU limits for each node in psci_non_cpu_pd_nodes */ 218*532ed618SSoby Mathew psci_update_pwrlvl_limits(); 219*532ed618SSoby Mathew 220*532ed618SSoby Mathew /* Populate the mpidr field of cpu node for this CPU */ 221*532ed618SSoby Mathew psci_cpu_pd_nodes[plat_my_core_pos()].mpidr = 222*532ed618SSoby Mathew read_mpidr() & MPIDR_AFFINITY_MASK; 223*532ed618SSoby Mathew 224*532ed618SSoby Mathew psci_init_req_local_pwr_states(); 225*532ed618SSoby Mathew 226*532ed618SSoby Mathew /* 227*532ed618SSoby Mathew * Set the requested and target state of this CPU and all the higher 228*532ed618SSoby Mathew * power domain levels for this CPU to run. 229*532ed618SSoby Mathew */ 230*532ed618SSoby Mathew psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL); 231*532ed618SSoby Mathew 232*532ed618SSoby Mathew plat_setup_psci_ops((uintptr_t)psci_entrypoint, 233*532ed618SSoby Mathew &psci_plat_pm_ops); 234*532ed618SSoby Mathew assert(psci_plat_pm_ops); 235*532ed618SSoby Mathew 236*532ed618SSoby Mathew /* Initialize the psci capability */ 237*532ed618SSoby Mathew psci_caps = PSCI_GENERIC_CAP; 238*532ed618SSoby Mathew 239*532ed618SSoby Mathew if (psci_plat_pm_ops->pwr_domain_off) 240*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_CPU_OFF); 241*532ed618SSoby Mathew if (psci_plat_pm_ops->pwr_domain_on && 242*532ed618SSoby Mathew psci_plat_pm_ops->pwr_domain_on_finish) 243*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); 244*532ed618SSoby Mathew if (psci_plat_pm_ops->pwr_domain_suspend && 245*532ed618SSoby Mathew psci_plat_pm_ops->pwr_domain_suspend_finish) { 246*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64); 247*532ed618SSoby Mathew if (psci_plat_pm_ops->get_sys_suspend_power_state) 248*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64); 249*532ed618SSoby Mathew } 250*532ed618SSoby Mathew if (psci_plat_pm_ops->system_off) 251*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_SYSTEM_OFF); 252*532ed618SSoby Mathew if (psci_plat_pm_ops->system_reset) 253*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET); 254*532ed618SSoby Mathew 255*532ed618SSoby Mathew #if ENABLE_PSCI_STAT 256*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64); 257*532ed618SSoby Mathew psci_caps |= define_psci_cap(PSCI_STAT_COUNT_AARCH64); 258*532ed618SSoby Mathew #endif 259*532ed618SSoby Mathew 260*532ed618SSoby Mathew return 0; 261*532ed618SSoby Mathew } 262