diff options
Diffstat (limited to 'services/std_svc/psci/psci_main.c')
-rw-r--r-- | services/std_svc/psci/psci_main.c | 212 |
1 files changed, 98 insertions, 114 deletions
diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c index b389287b..f0242910 100644 --- a/services/std_svc/psci/psci_main.c +++ b/services/std_svc/psci/psci_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,6 +35,7 @@ #include <platform.h> #include <runtime_svc.h> #include <std_svc.h> +#include <string.h> #include "psci_private.h" /******************************************************************************* @@ -46,14 +47,13 @@ int psci_cpu_on(unsigned long target_cpu, { int rc; - unsigned int start_afflvl, end_afflvl; + unsigned int end_pwrlvl; entry_point_info_t ep; /* Determine if the cpu exists of not */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); - if (rc != PSCI_E_SUCCESS) { + rc = psci_validate_mpidr(target_cpu); + if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; - } /* Validate the entrypoint using platform pm_ops */ if (psci_plat_pm_ops->validate_ns_entrypoint) { @@ -73,18 +73,14 @@ int psci_cpu_on(unsigned long target_cpu, if (rc != PSCI_E_SUCCESS) return rc; - /* - * To turn this cpu on, specify which affinity + * To turn this cpu on, specify which power * levels need to be turned on */ - start_afflvl = MPIDR_AFFLVL0; - end_afflvl = PLATFORM_MAX_AFFLVL; - rc = psci_afflvl_on(target_cpu, + end_pwrlvl = PLAT_MAX_PWR_LVL; + rc = psci_cpu_on_start(target_cpu, &ep, - start_afflvl, - end_afflvl); - + end_pwrlvl); return rc; } @@ -98,73 +94,82 @@ int psci_cpu_suspend(unsigned int power_state, unsigned long context_id) { int rc; - unsigned int target_afflvl, pstate_type; + unsigned int target_pwrlvl, is_power_down_state; entry_point_info_t ep; + psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; + plat_local_state_t cpu_pd_state; - /* Check SBZ bits in power state are zero */ - if (psci_validate_power_state(power_state)) - return PSCI_E_INVALID_PARAMS; + /* Validate the power_state parameter */ + rc = psci_validate_power_state(power_state, &state_info); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; + } - /* Sanity check the requested state */ - target_afflvl = psci_get_pstate_afflvl(power_state); - if (target_afflvl > PLATFORM_MAX_AFFLVL) - return PSCI_E_INVALID_PARAMS; + /* + * Get the value of the state type bit from the power state parameter. + */ + is_power_down_state = psci_get_pstate_type(power_state); - /* Validate the power_state using platform pm_ops */ - if (psci_plat_pm_ops->validate_power_state) { - rc = psci_plat_pm_ops->validate_power_state(power_state); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } - } + /* Sanity check the requested suspend levels */ + assert (psci_validate_suspend_req(&state_info, is_power_down_state) + == PSCI_E_SUCCESS); - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); + target_pwrlvl = psci_find_target_suspend_lvl(&state_info); + + /* Fast path for CPU standby.*/ + if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) { + if (!psci_plat_pm_ops->cpu_standby) return PSCI_E_INVALID_PARAMS; - } - } - /* Determine the 'state type' in the 'power_state' parameter */ - pstate_type = psci_get_pstate_type(power_state); + /* + * Set the state of the CPU power domain to the platform + * specific retention state and enter the standby state. + */ + cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL]; + psci_set_cpu_local_state(cpu_pd_state); + psci_plat_pm_ops->cpu_standby(cpu_pd_state); - /* - * Ensure that we have a platform specific handler for entering - * a standby state. - */ - if (pstate_type == PSTATE_TYPE_STANDBY) { - if (!psci_plat_pm_ops->affinst_standby) - return PSCI_E_INVALID_PARAMS; + /* Upon exit from standby, set the state back to RUN. */ + psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); - psci_plat_pm_ops->affinst_standby(power_state); return PSCI_E_SUCCESS; } /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. + * If a power down state has been requested, we need to verify entry + * point and program entry information. */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; + if (is_power_down_state) { + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; + } + } - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + } /* * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this CPU. + * enter the final wfi which will power down this CPU. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ - psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, - target_afflvl); + psci_cpu_suspend_start(&ep, + target_pwrlvl, + &state_info, + is_power_down_state); - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } @@ -172,7 +177,7 @@ int psci_system_suspend(unsigned long entrypoint, unsigned long context_id) { int rc; - unsigned int power_state; + psci_power_state_t state_info; entry_point_info_t ep; /* Validate the entrypoint using platform pm_ops */ @@ -180,7 +185,7 @@ int psci_system_suspend(unsigned long entrypoint, rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); if (rc != PSCI_E_SUCCESS) { assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; + return rc; } } @@ -197,45 +202,39 @@ int psci_system_suspend(unsigned long entrypoint, if (rc != PSCI_E_SUCCESS) return rc; - /* - * Assert that the required pm_ops hook is implemented to ensure that - * the capability detected during psci_setup() is valid. - */ - assert(psci_plat_pm_ops->get_sys_suspend_power_state); + /* Query the psci_power_state for system suspend */ + psci_query_sys_suspend_pwrstate(&state_info); - /* - * Query the platform for the power_state required for system suspend - */ - power_state = psci_plat_pm_ops->get_sys_suspend_power_state(); - - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); + /* Ensure that the psci_power_state makes sense */ + assert(psci_find_target_suspend_lvl(&state_info) == PLAT_MAX_PWR_LVL); + assert(psci_validate_suspend_req(&state_info, PSTATE_TYPE_POWERDOWN) + == PSCI_E_SUCCESS); + assert(is_local_state_off(state_info.pwr_domain_state[PLAT_MAX_PWR_LVL])); /* - * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this cpu. + * Do what is needed to enter the system suspend state. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ - psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, - PLATFORM_MAX_AFFLVL); + psci_cpu_suspend_start(&ep, + PLAT_MAX_PWR_LVL, + &state_info, + PSTATE_TYPE_POWERDOWN); - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } int psci_cpu_off(void) { int rc; - int target_afflvl = PLATFORM_MAX_AFFLVL; + int target_pwrlvl = PLAT_MAX_PWR_LVL; /* - * Traverse from the highest to the lowest affinity level. When the - * lowest affinity level is hit, all the locks are acquired. State - * management is done immediately followed by cpu, cluster ... - * ..target_afflvl specific actions as this function unwinds back. + * Do what is needed to power off this CPU and possible higher power + * levels if it able to do so. Upon success, enter the final wfi + * which will power down this CPU. */ - rc = psci_afflvl_off(MPIDR_AFFLVL0, target_afflvl); + rc = psci_do_cpu_off(target_pwrlvl); /* * The only error cpu_off can return is E_DENIED. So check if that's @@ -249,32 +248,18 @@ int psci_cpu_off(void) int psci_affinity_info(unsigned long target_affinity, unsigned int lowest_affinity_level) { - int rc = PSCI_E_INVALID_PARAMS; - unsigned int aff_state; - aff_map_node_t *node; - - if (lowest_affinity_level > PLATFORM_MAX_AFFLVL) - return rc; - - node = psci_get_aff_map_node(target_affinity, lowest_affinity_level); - if (node && (node->state & PSCI_AFF_PRESENT)) { - - /* - * TODO: For affinity levels higher than 0 i.e. cpu, the - * state will always be either ON or OFF. Need to investigate - * how critical is it to support ON_PENDING here. - */ - aff_state = psci_get_state(node); + unsigned int target_idx; - /* A suspended cpu is available & on for the OS */ - if (aff_state == PSCI_STATE_SUSPEND) { - aff_state = PSCI_STATE_ON; - } + /* We dont support level higher than PSCI_CPU_PWR_LVL */ + if (lowest_affinity_level > PSCI_CPU_PWR_LVL) + return PSCI_E_INVALID_PARAMS; - rc = aff_state; - } + /* Calculate the cpu index of the target */ + target_idx = plat_core_pos_by_mpidr(target_affinity); + if (target_idx == -1) + return PSCI_E_INVALID_PARAMS; - return rc; + return psci_get_aff_info_state_by_idx(target_idx); } int psci_migrate(unsigned long target_cpu) @@ -295,7 +280,7 @@ int psci_migrate(unsigned long target_cpu) return PSCI_E_NOT_PRESENT; /* Check the validity of the specified target cpu */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); + rc = psci_validate_mpidr(target_cpu); if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; @@ -352,10 +337,9 @@ int psci_features(unsigned int psci_fid) if (psci_fid == PSCI_CPU_SUSPEND_AARCH32 || psci_fid == PSCI_CPU_SUSPEND_AARCH64) { /* - * The trusted firmware uses the original power state format - * and does not support OS Initiated Mode. + * The trusted firmware does not support OS Initiated Mode. */ - return (FF_PSTATE_ORIG << FF_PSTATE_SHIFT) | + return (FF_PSTATE << FF_PSTATE_SHIFT) | ((!FF_SUPPORTS_OS_INIT_MODE) << FF_MODE_SUPPORT_SHIFT); } |