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