12d4135e0SAntonio Nino Diaz /* 22d4135e0SAntonio Nino Diaz * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. 32d4135e0SAntonio Nino Diaz * 42d4135e0SAntonio Nino Diaz * SPDX-License-Identifier: BSD-3-Clause 52d4135e0SAntonio Nino Diaz */ 62d4135e0SAntonio Nino Diaz 72d4135e0SAntonio Nino Diaz #include <assert.h> 82d4135e0SAntonio Nino Diaz 92d4135e0SAntonio Nino Diaz #include <arch_helpers.h> 102d4135e0SAntonio Nino Diaz #include <common/debug.h> 112d4135e0SAntonio Nino Diaz #include <drivers/arm/css/css_scp.h> 122d4135e0SAntonio Nino Diaz #include <drivers/arm/css/css_scpi.h> 132d4135e0SAntonio Nino Diaz #include <plat/arm/common/plat_arm.h> 142d4135e0SAntonio Nino Diaz #include <plat/arm/css/common/css_pm.h> 152d4135e0SAntonio Nino Diaz 162d4135e0SAntonio Nino Diaz /* 172d4135e0SAntonio Nino Diaz * This file implements the SCP power management functions using SCPI protocol. 182d4135e0SAntonio Nino Diaz */ 192d4135e0SAntonio Nino Diaz 202d4135e0SAntonio Nino Diaz /* 212d4135e0SAntonio Nino Diaz * Helper function to inform power down state to SCP. 222d4135e0SAntonio Nino Diaz */ 232d4135e0SAntonio Nino Diaz void css_scp_suspend(const struct psci_power_state *target_state) 242d4135e0SAntonio Nino Diaz { 252d4135e0SAntonio Nino Diaz uint32_t cluster_state = scpi_power_on; 262d4135e0SAntonio Nino Diaz uint32_t system_state = scpi_power_on; 272d4135e0SAntonio Nino Diaz 282d4135e0SAntonio Nino Diaz /* Check if power down at system power domain level is requested */ 292d4135e0SAntonio Nino Diaz if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) 302d4135e0SAntonio Nino Diaz system_state = scpi_power_retention; 312d4135e0SAntonio Nino Diaz 322d4135e0SAntonio Nino Diaz /* Cluster is to be turned off, so disable coherency */ 332d4135e0SAntonio Nino Diaz if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) 342d4135e0SAntonio Nino Diaz cluster_state = scpi_power_off; 352d4135e0SAntonio Nino Diaz 362d4135e0SAntonio Nino Diaz /* 372d4135e0SAntonio Nino Diaz * Ask the SCP to power down the appropriate components depending upon 382d4135e0SAntonio Nino Diaz * their state. 392d4135e0SAntonio Nino Diaz */ 402d4135e0SAntonio Nino Diaz scpi_set_css_power_state(read_mpidr_el1(), 412d4135e0SAntonio Nino Diaz scpi_power_off, 422d4135e0SAntonio Nino Diaz cluster_state, 432d4135e0SAntonio Nino Diaz system_state); 442d4135e0SAntonio Nino Diaz } 452d4135e0SAntonio Nino Diaz 462d4135e0SAntonio Nino Diaz /* 472d4135e0SAntonio Nino Diaz * Helper function to turn off a CPU power domain and its parent power domains 482d4135e0SAntonio Nino Diaz * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we 492d4135e0SAntonio Nino Diaz * call the suspend helper here. 502d4135e0SAntonio Nino Diaz */ 512d4135e0SAntonio Nino Diaz void css_scp_off(const struct psci_power_state *target_state) 522d4135e0SAntonio Nino Diaz { 532d4135e0SAntonio Nino Diaz css_scp_suspend(target_state); 542d4135e0SAntonio Nino Diaz } 552d4135e0SAntonio Nino Diaz 562d4135e0SAntonio Nino Diaz /* 572d4135e0SAntonio Nino Diaz * Helper function to turn ON a CPU power domain and its parent power domains 582d4135e0SAntonio Nino Diaz * if applicable. 592d4135e0SAntonio Nino Diaz */ 602d4135e0SAntonio Nino Diaz void css_scp_on(u_register_t mpidr) 612d4135e0SAntonio Nino Diaz { 622d4135e0SAntonio Nino Diaz /* 632d4135e0SAntonio Nino Diaz * SCP takes care of powering up parent power domains so we 642d4135e0SAntonio Nino Diaz * only need to care about level 0 652d4135e0SAntonio Nino Diaz */ 662d4135e0SAntonio Nino Diaz scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, 672d4135e0SAntonio Nino Diaz scpi_power_on); 682d4135e0SAntonio Nino Diaz } 692d4135e0SAntonio Nino Diaz 702d4135e0SAntonio Nino Diaz /* 712d4135e0SAntonio Nino Diaz * Helper function to get the power state of a power domain node as reported 722d4135e0SAntonio Nino Diaz * by the SCP. 732d4135e0SAntonio Nino Diaz */ 742d4135e0SAntonio Nino Diaz int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) 752d4135e0SAntonio Nino Diaz { 762d4135e0SAntonio Nino Diaz int rc, element; 772d4135e0SAntonio Nino Diaz unsigned int cpu_state, cluster_state; 782d4135e0SAntonio Nino Diaz 792d4135e0SAntonio Nino Diaz /* 802d4135e0SAntonio Nino Diaz * The format of 'power_level' is implementation-defined, but 0 must 812d4135e0SAntonio Nino Diaz * mean a CPU. We also allow 1 to denote the cluster 822d4135e0SAntonio Nino Diaz */ 832d4135e0SAntonio Nino Diaz if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) 842d4135e0SAntonio Nino Diaz return PSCI_E_INVALID_PARAMS; 852d4135e0SAntonio Nino Diaz 862d4135e0SAntonio Nino Diaz /* Query SCP */ 872d4135e0SAntonio Nino Diaz rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state); 882d4135e0SAntonio Nino Diaz if (rc != 0) 892d4135e0SAntonio Nino Diaz return PSCI_E_INVALID_PARAMS; 902d4135e0SAntonio Nino Diaz 912d4135e0SAntonio Nino Diaz /* Map power states of CPU and cluster to expected PSCI return codes */ 922d4135e0SAntonio Nino Diaz if (power_level == ARM_PWR_LVL0) { 932d4135e0SAntonio Nino Diaz /* 942d4135e0SAntonio Nino Diaz * The CPU state returned by SCP is an 8-bit bit mask 952d4135e0SAntonio Nino Diaz * corresponding to each CPU in the cluster 962d4135e0SAntonio Nino Diaz */ 972d4135e0SAntonio Nino Diaz #if ARM_PLAT_MT 982d4135e0SAntonio Nino Diaz /* 992d4135e0SAntonio Nino Diaz * The current SCPI driver only caters for single-threaded 1002d4135e0SAntonio Nino Diaz * platforms. Hence we ignore the thread ID (which is always 0) 1012d4135e0SAntonio Nino Diaz * for such platforms. 1022d4135e0SAntonio Nino Diaz */ 1032d4135e0SAntonio Nino Diaz element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; 1042d4135e0SAntonio Nino Diaz #else 1052d4135e0SAntonio Nino Diaz element = mpidr & MPIDR_AFFLVL_MASK; 1062d4135e0SAntonio Nino Diaz #endif /* ARM_PLAT_MT */ 1072d4135e0SAntonio Nino Diaz return CSS_CPU_PWR_STATE(cpu_state, element) == 1082d4135e0SAntonio Nino Diaz CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF; 1092d4135e0SAntonio Nino Diaz } else { 1102d4135e0SAntonio Nino Diaz assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON || 1112d4135e0SAntonio Nino Diaz cluster_state == CSS_CLUSTER_PWR_STATE_OFF); 1122d4135e0SAntonio Nino Diaz return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON : 1132d4135e0SAntonio Nino Diaz HW_OFF; 1142d4135e0SAntonio Nino Diaz } 1152d4135e0SAntonio Nino Diaz } 1162d4135e0SAntonio Nino Diaz 1172d4135e0SAntonio Nino Diaz /* 1182d4135e0SAntonio Nino Diaz * Helper function to shutdown the system via SCPI. 1192d4135e0SAntonio Nino Diaz */ 120*da305ec7SBoyan Karatotev void css_scp_sys_shutdown(void) 1212d4135e0SAntonio Nino Diaz { 1222d4135e0SAntonio Nino Diaz uint32_t response; 1232d4135e0SAntonio Nino Diaz 1242d4135e0SAntonio Nino Diaz /* 1252d4135e0SAntonio Nino Diaz * Disable GIC CPU interface to prevent pending interrupt 1262d4135e0SAntonio Nino Diaz * from waking up the AP from WFI. 1272d4135e0SAntonio Nino Diaz */ 1282d4135e0SAntonio Nino Diaz plat_arm_gic_cpuif_disable(); 1292d4135e0SAntonio Nino Diaz 1302d4135e0SAntonio Nino Diaz /* Send the power down request to the SCP */ 1312d4135e0SAntonio Nino Diaz response = scpi_sys_power_state(scpi_system_shutdown); 1322d4135e0SAntonio Nino Diaz 1332d4135e0SAntonio Nino Diaz if (response != SCP_OK) { 1342d4135e0SAntonio Nino Diaz ERROR("CSS System Off: SCP error %u.\n", response); 1352d4135e0SAntonio Nino Diaz panic(); 1362d4135e0SAntonio Nino Diaz } 1372d4135e0SAntonio Nino Diaz } 1382d4135e0SAntonio Nino Diaz 1392d4135e0SAntonio Nino Diaz /* 1402d4135e0SAntonio Nino Diaz * Helper function to reset the system via SCPI. 1412d4135e0SAntonio Nino Diaz */ 142*da305ec7SBoyan Karatotev void css_scp_sys_reboot(void) 1432d4135e0SAntonio Nino Diaz { 1442d4135e0SAntonio Nino Diaz uint32_t response; 1452d4135e0SAntonio Nino Diaz 1462d4135e0SAntonio Nino Diaz /* 1472d4135e0SAntonio Nino Diaz * Disable GIC CPU interface to prevent pending interrupt 1482d4135e0SAntonio Nino Diaz * from waking up the AP from WFI. 1492d4135e0SAntonio Nino Diaz */ 1502d4135e0SAntonio Nino Diaz plat_arm_gic_cpuif_disable(); 1512d4135e0SAntonio Nino Diaz 1522d4135e0SAntonio Nino Diaz /* Send the system reset request to the SCP */ 1532d4135e0SAntonio Nino Diaz response = scpi_sys_power_state(scpi_system_reboot); 1542d4135e0SAntonio Nino Diaz 1552d4135e0SAntonio Nino Diaz if (response != SCP_OK) { 1562d4135e0SAntonio Nino Diaz ERROR("CSS System Reset: SCP error %u.\n", response); 1572d4135e0SAntonio Nino Diaz panic(); 1582d4135e0SAntonio Nino Diaz } 1592d4135e0SAntonio Nino Diaz } 160