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