diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 3c8a811..41a5f0a 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -140,10 +140,14 @@ /* Security utility functions */ void arm_tzc_setup(void); +/* Systimer utility function */ +void arm_configure_sys_timer(void); + /* PM utility functions */ int arm_validate_power_state(unsigned int power_state, psci_power_state_t *req_state); int arm_validate_ns_entrypoint(uintptr_t entrypoint); +void arm_system_pwr_domain_resume(void); /* Topology utility function */ int arm_check_mpidr(u_register_t mpidr); diff --git a/include/plat/arm/css/common/css_pm.h b/include/plat/arm/css/common/css_pm.h index c19df92..ea6a5d2 100644 --- a/include/plat/arm/css/common/css_pm.h +++ b/include/plat/arm/css/common/css_pm.h @@ -44,5 +44,6 @@ void __dead2 css_system_off(void); void __dead2 css_system_reset(void); void css_cpu_standby(plat_local_state_t cpu_state); +void css_get_sys_suspend_power_state(psci_power_state_t *req_state); #endif /* __CSS_PM_H__ */ diff --git a/plat/arm/board/juno/juno_pm.c b/plat/arm/board/juno/juno_pm.c new file mode 100644 index 0000000..4b956d2 --- /dev/null +++ b/plat/arm/board/juno/juno_pm.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include + +/* + * Custom `validate_power_state` handler for Juno. According to PSCI + * Specification, interrupts targeted to cores in PSCI CPU SUSPEND should + * be able to resume it. On Juno, when the system power domain is suspended, + * the GIC is also powered down. The SCP resumes the final core to be suspend + * when an external wake-up event is received. But the other cores cannot be + * woken up by a targeted interrupt, because GIC doesn't forward these + * interrupts to the SCP. Due to this hardware limitation, we down-grade PSCI + * CPU SUSPEND requests targeted to the system power domain level + * to cluster power domain level. + * + * The system power domain suspend on Juno is only supported only via + * PSCI SYSTEM SUSPEND API. + */ +static int juno_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int rc; + rc = arm_validate_power_state(power_state, req_state); + + /* + * Ensure that the system power domain level is never suspended + * via PSCI CPU SUSPEND API. Currently system suspend is only + * supported via PSCI SYSTEM SUSPEND API. + */ + req_state->pwr_domain_state[ARM_PWR_LVL2] = ARM_LOCAL_STATE_RUN; + return rc; +} + +/******************************************************************************* + * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard + * platform will take care of registering the handlers with PSCI. + ******************************************************************************/ +const plat_psci_ops_t plat_arm_psci_pm_ops = { + .pwr_domain_on = css_pwr_domain_on, + .pwr_domain_on_finish = css_pwr_domain_on_finish, + .pwr_domain_off = css_pwr_domain_off, + .cpu_standby = css_cpu_standby, + .pwr_domain_suspend = css_pwr_domain_suspend, + .pwr_domain_suspend_finish = css_pwr_domain_suspend_finish, + .system_off = css_system_off, + .system_reset = css_system_reset, + .validate_power_state = juno_validate_power_state, + .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .get_sys_suspend_power_state = css_get_sys_suspend_power_state +}; diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk index b80cfb3..0dde4d7 100644 --- a/plat/arm/board/juno/platform.mk +++ b/plat/arm/board/juno/platform.mk @@ -38,7 +38,9 @@ BL2_SOURCES += plat/arm/board/juno/juno_security.c \ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ - lib/cpus/aarch64/cortex_a57.S + lib/cpus/aarch64/cortex_a57.S \ + plat/arm/board/juno/juno_pm.c \ + plat/arm/board/juno/juno_security.c # Enable workarounds for selected Cortex-A57 erratas. ERRATA_A57_806969 := 0 diff --git a/plat/arm/common/aarch64/arm_common.c b/plat/arm/common/aarch64/arm_common.c index 48b4ac8..4264183 100644 --- a/plat/arm/common/aarch64/arm_common.c +++ b/plat/arm/common/aarch64/arm_common.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -142,3 +143,19 @@ { cci_init(PLAT_ARM_CCI_BASE, cci_map, ARRAY_SIZE(cci_map)); } + +/******************************************************************************* + * Configures access to the system counter timer module. + ******************************************************************************/ +void arm_configure_sys_timer(void) +{ + unsigned int reg_val; + + reg_val = (1 << CNTACR_RPCT_SHIFT) | (1 << CNTACR_RVCT_SHIFT); + reg_val |= (1 << CNTACR_RFRQ_SHIFT) | (1 << CNTACR_RVOFF_SHIFT); + reg_val |= (1 << CNTACR_RWVT_SHIFT) | (1 << CNTACR_RWPT_SHIFT); + mmio_write_32(ARM_SYS_TIMCTL_BASE + CNTACR_BASE(PLAT_ARM_NSTIMER_FRAME_ID), reg_val); + + reg_val = (1 << CNTNSAR_NS_SHIFT(PLAT_ARM_NSTIMER_FRAME_ID)); + mmio_write_32(ARM_SYS_TIMCTL_BASE + CNTNSAR, reg_val); +} diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index 899463e..923c333 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -40,7 +40,6 @@ #include #include #include -#include /* @@ -197,8 +196,6 @@ ******************************************************************************/ void arm_bl31_platform_setup(void) { - unsigned int reg_val; - /* Initialize the gic cpu and distributor interfaces */ plat_arm_gic_init(); arm_gic_setup(); @@ -217,13 +214,7 @@ CNTCR_FCREQ(0) | CNTCR_EN); /* Allow access to the System counter timer module */ - reg_val = (1 << CNTACR_RPCT_SHIFT) | (1 << CNTACR_RVCT_SHIFT); - reg_val |= (1 << CNTACR_RFRQ_SHIFT) | (1 << CNTACR_RVOFF_SHIFT); - reg_val |= (1 << CNTACR_RWVT_SHIFT) | (1 << CNTACR_RWPT_SHIFT); - mmio_write_32(ARM_SYS_TIMCTL_BASE + CNTACR_BASE(PLAT_ARM_NSTIMER_FRAME_ID), reg_val); - - reg_val = (1 << CNTNSAR_NS_SHIFT(PLAT_ARM_NSTIMER_FRAME_ID)); - mmio_write_32(ARM_SYS_TIMCTL_BASE + CNTNSAR, reg_val); + arm_configure_sys_timer(); /* Initialize power controller before setting up topology */ plat_arm_pwrc_setup(); diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 2497588..d13d268 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -30,7 +30,9 @@ #include #include +#include #include +#include #include #include #include @@ -148,6 +150,26 @@ return PSCI_E_INVALID_ADDRESS; } +/****************************************************************************** + * Helper function to resume the platform from system suspend. Reinitialize + * the system components which are not in the Always ON power domain. + * TODO: Unify the platform setup when waking up from cold boot and system + * resume in arm_bl31_platform_setup(). + *****************************************************************************/ +void arm_system_pwr_domain_resume(void) +{ + console_init(PLAT_ARM_BOOT_UART_BASE, PLAT_ARM_BOOT_UART_CLK_IN_HZ, + ARM_CONSOLE_BAUDRATE); + + /* Assert system power domain is available on the platform */ + assert(PLAT_MAX_PWR_LVL >= ARM_PWR_LVL2); + + arm_gic_setup(); + plat_arm_security_setup(); + + arm_configure_sys_timer(); +} + /******************************************************************************* * Private function to program the mailbox for a cpu before it is released * from reset. This function assumes that the Trusted mail box base is within diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 2d0e901..3f46857 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,13 @@ }; #endif /* __ARM_RECOM_STATE_ID_ENC__ */ +/* + * All the power management helpers in this file assume at least cluster power + * level is supported. + */ +CASSERT(PLAT_MAX_PWR_LVL >= ARM_PWR_LVL1, + assert_max_pwr_lvl_supported_mismatch); + /******************************************************************************* * Handler called when a power domain is about to be turned on. The * level and mpidr determine the affinity instance. @@ -95,6 +103,16 @@ assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == ARM_LOCAL_STATE_OFF); + if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { + /* + * Perform system initialization if woken up from system + * suspend. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL2] == + ARM_LOCAL_STATE_OFF) + arm_system_pwr_domain_resume(); + } + /* * Perform the common cluster specific operations i.e enable coherency * if this cluster was off. @@ -103,6 +121,18 @@ ARM_LOCAL_STATE_OFF) cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); + + if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { + /* + * Skip GIC CPU interface and per-CPU Distributor interface + * setups if woken up from system suspend as it is done as + * part of css_system_pwr_domain_resume(). + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL2] == + ARM_LOCAL_STATE_OFF) + return; + } + /* Enable the gic cpu interface */ arm_gic_cpuif_setup(); @@ -119,10 +149,21 @@ static void css_power_down_common(const psci_power_state_t *target_state) { uint32_t cluster_state = scpi_power_on; + uint32_t system_state = scpi_power_on; /* Prevent interrupts from spuriously waking up this cpu */ arm_gic_cpuif_deactivate(); + if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { + /* + * Check if power down at system power domain level is + * requested. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL2] == + ARM_LOCAL_STATE_OFF) + system_state = scpi_power_retention; + } + /* Cluster is to be turned off, so disable coherency */ if (target_state->pwr_domain_state[ARM_PWR_LVL1] == ARM_LOCAL_STATE_OFF) { @@ -137,7 +178,7 @@ scpi_set_css_power_state(read_mpidr_el1(), scpi_power_off, cluster_state, - scpi_power_on); + system_state); } /******************************************************************************* @@ -251,6 +292,23 @@ } /******************************************************************************* + * Handler called to return the 'req_state' for system suspend. + ******************************************************************************/ +void css_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + unsigned int i; + + /* + * System Suspend is supported only if the system power domain node + * is implemented. + */ + assert(PLAT_MAX_PWR_LVL >= ARM_PWR_LVL2); + + for (i = ARM_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF; +} + +/******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform will take care of registering the handlers with PSCI. ******************************************************************************/ diff --git a/plat/arm/soc/common/soc_css.mk b/plat/arm/soc/common/soc_css.mk index fd51b7f..d8a99a8 100644 --- a/plat/arm/soc/common/soc_css.mk +++ b/plat/arm/soc/common/soc_css.mk @@ -37,4 +37,4 @@ BL2_SOURCES += plat/arm/soc/common/soc_css_security.c -#BL31_SOURCES += +BL31_SOURCES += plat/arm/soc/common/soc_css_security.c