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