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