13fc4124cSDan Handley /* 23fc4124cSDan Handley * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. 33fc4124cSDan Handley * 43fc4124cSDan Handley * Redistribution and use in source and binary forms, with or without 53fc4124cSDan Handley * modification, are permitted provided that the following conditions are met: 63fc4124cSDan Handley * 73fc4124cSDan Handley * Redistributions of source code must retain the above copyright notice, this 83fc4124cSDan Handley * list of conditions and the following disclaimer. 93fc4124cSDan Handley * 103fc4124cSDan Handley * Redistributions in binary form must reproduce the above copyright notice, 113fc4124cSDan Handley * this list of conditions and the following disclaimer in the documentation 123fc4124cSDan Handley * and/or other materials provided with the distribution. 133fc4124cSDan Handley * 143fc4124cSDan Handley * Neither the name of ARM nor the names of its contributors may be used 153fc4124cSDan Handley * to endorse or promote products derived from this software without specific 163fc4124cSDan Handley * prior written permission. 173fc4124cSDan Handley * 183fc4124cSDan Handley * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 193fc4124cSDan Handley * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 203fc4124cSDan Handley * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 213fc4124cSDan Handley * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 223fc4124cSDan Handley * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 233fc4124cSDan Handley * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 243fc4124cSDan Handley * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 253fc4124cSDan Handley * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 263fc4124cSDan Handley * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 273fc4124cSDan Handley * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 283fc4124cSDan Handley * POSSIBILITY OF SUCH DAMAGE. 293fc4124cSDan Handley */ 303fc4124cSDan Handley 313fc4124cSDan Handley #include <arch_helpers.h> 323fc4124cSDan Handley #include <arm_config.h> 333fc4124cSDan Handley #include <arm_gic.h> 343fc4124cSDan Handley #include <assert.h> 353fc4124cSDan Handley #include <debug.h> 363fc4124cSDan Handley #include <errno.h> 373fc4124cSDan Handley #include <mmio.h> 383fc4124cSDan Handley #include <platform.h> 393fc4124cSDan Handley #include <plat_arm.h> 403fc4124cSDan Handley #include <psci.h> 413fc4124cSDan Handley #include <v2m_def.h> 423fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h" 433fc4124cSDan Handley #include "fvp_def.h" 443fc4124cSDan Handley #include "fvp_private.h" 453fc4124cSDan Handley 46*38dce70fSSoby Mathew unsigned long wakeup_address; 473fc4124cSDan Handley 483fc4124cSDan Handley typedef volatile struct mailbox { 493fc4124cSDan Handley unsigned long value __aligned(CACHE_WRITEBACK_GRANULE); 503fc4124cSDan Handley } mailbox_t; 513fc4124cSDan Handley 523fc4124cSDan Handley /******************************************************************************* 533fc4124cSDan Handley * Private FVP function to program the mailbox for a cpu before it is released 543fc4124cSDan Handley * from reset. 553fc4124cSDan Handley ******************************************************************************/ 563fc4124cSDan Handley static void fvp_program_mailbox(uint64_t mpidr, uint64_t address) 573fc4124cSDan Handley { 583fc4124cSDan Handley uint64_t linear_id; 593fc4124cSDan Handley mailbox_t *fvp_mboxes; 603fc4124cSDan Handley 61*38dce70fSSoby Mathew linear_id = plat_arm_calc_core_pos(mpidr); 623fc4124cSDan Handley fvp_mboxes = (mailbox_t *)MBOX_BASE; 633fc4124cSDan Handley fvp_mboxes[linear_id].value = address; 643fc4124cSDan Handley flush_dcache_range((unsigned long) &fvp_mboxes[linear_id], 653fc4124cSDan Handley sizeof(unsigned long)); 663fc4124cSDan Handley } 673fc4124cSDan Handley 683fc4124cSDan Handley /******************************************************************************* 693fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 703fc4124cSDan Handley * cpu in response to a CPU_OFF or CPU_SUSPEND request. 713fc4124cSDan Handley ******************************************************************************/ 723fc4124cSDan Handley static void fvp_cpu_pwrdwn_common(void) 733fc4124cSDan Handley { 743fc4124cSDan Handley /* Prevent interrupts from spuriously waking up this cpu */ 753fc4124cSDan Handley arm_gic_cpuif_deactivate(); 763fc4124cSDan Handley 773fc4124cSDan Handley /* Program the power controller to power off this cpu. */ 783fc4124cSDan Handley fvp_pwrc_write_ppoffr(read_mpidr_el1()); 793fc4124cSDan Handley } 803fc4124cSDan Handley 813fc4124cSDan Handley /******************************************************************************* 823fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 833fc4124cSDan Handley * cluster in response to a CPU_OFF or CPU_SUSPEND request. 843fc4124cSDan Handley ******************************************************************************/ 853fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void) 863fc4124cSDan Handley { 873fc4124cSDan Handley uint64_t mpidr = read_mpidr_el1(); 883fc4124cSDan Handley 893fc4124cSDan Handley /* Disable coherency if this cluster is to be turned off */ 903fc4124cSDan Handley fvp_cci_disable(); 913fc4124cSDan Handley 923fc4124cSDan Handley /* Program the power controller to turn the cluster off */ 933fc4124cSDan Handley fvp_pwrc_write_pcoffr(mpidr); 943fc4124cSDan Handley } 953fc4124cSDan Handley 963fc4124cSDan Handley /******************************************************************************* 97*38dce70fSSoby Mathew * FVP handler called when a CPU is about to enter standby. 983fc4124cSDan Handley ******************************************************************************/ 99*38dce70fSSoby Mathew void fvp_cpu_standby(plat_local_state_t cpu_state) 1003fc4124cSDan Handley { 101*38dce70fSSoby Mathew 102*38dce70fSSoby Mathew assert(cpu_state == ARM_LOCAL_STATE_RET); 103*38dce70fSSoby Mathew 1043fc4124cSDan Handley /* 1053fc4124cSDan Handley * Enter standby state 1063fc4124cSDan Handley * dsb is good practice before using wfi to enter low power states 1073fc4124cSDan Handley */ 1083fc4124cSDan Handley dsb(); 1093fc4124cSDan Handley wfi(); 1103fc4124cSDan Handley } 1113fc4124cSDan Handley 1123fc4124cSDan Handley /******************************************************************************* 113*38dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned on. The 114*38dce70fSSoby Mathew * mpidr determines the CPU to be turned on. 1153fc4124cSDan Handley ******************************************************************************/ 116*38dce70fSSoby Mathew int fvp_pwr_domain_on(u_register_t mpidr) 1173fc4124cSDan Handley { 1183fc4124cSDan Handley int rc = PSCI_E_SUCCESS; 1193fc4124cSDan Handley unsigned int psysr; 1203fc4124cSDan Handley 1213fc4124cSDan Handley /* 1223fc4124cSDan Handley * Ensure that we do not cancel an inflight power off request 1233fc4124cSDan Handley * for the target cpu. That would leave it in a zombie wfi. 1243fc4124cSDan Handley * Wait for it to power off, program the jump address for the 1253fc4124cSDan Handley * target cpu and then program the power controller to turn 1263fc4124cSDan Handley * that cpu on 1273fc4124cSDan Handley */ 1283fc4124cSDan Handley do { 1293fc4124cSDan Handley psysr = fvp_pwrc_read_psysr(mpidr); 1303fc4124cSDan Handley } while (psysr & PSYSR_AFF_L0); 1313fc4124cSDan Handley 132*38dce70fSSoby Mathew fvp_program_mailbox(mpidr, wakeup_address); 1333fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 1343fc4124cSDan Handley 1353fc4124cSDan Handley return rc; 1363fc4124cSDan Handley } 1373fc4124cSDan Handley 1383fc4124cSDan Handley /******************************************************************************* 139*38dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned off. The 140*38dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1413fc4124cSDan Handley ******************************************************************************/ 142*38dce70fSSoby Mathew void fvp_pwr_domain_off(const psci_power_state_t *target_state) 1433fc4124cSDan Handley { 144*38dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 145*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 1463fc4124cSDan Handley 1473fc4124cSDan Handley /* 148*38dce70fSSoby Mathew * If execution reaches this stage then this power domain will be 149*38dce70fSSoby Mathew * suspended. Perform at least the cpu specific actions followed 150*38dce70fSSoby Mathew * by the cluster specific operations if applicable. 1513fc4124cSDan Handley */ 1523fc4124cSDan Handley fvp_cpu_pwrdwn_common(); 1533fc4124cSDan Handley 154*38dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 155*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) 1563fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 1573fc4124cSDan Handley 1583fc4124cSDan Handley } 1593fc4124cSDan Handley 1603fc4124cSDan Handley /******************************************************************************* 161*38dce70fSSoby Mathew * FVP handler called when a power domain is about to be suspended. The 162*38dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1633fc4124cSDan Handley ******************************************************************************/ 164*38dce70fSSoby Mathew void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) 1653fc4124cSDan Handley { 1663fc4124cSDan Handley unsigned long mpidr; 1673fc4124cSDan Handley 168*38dce70fSSoby Mathew /* 169*38dce70fSSoby Mathew * FVP has retention only at cpu level. Just return 170*38dce70fSSoby Mathew * as nothing is to be done for retention. 171*38dce70fSSoby Mathew */ 172*38dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 173*38dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 1743fc4124cSDan Handley return; 1753fc4124cSDan Handley 176*38dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 177*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 178*38dce70fSSoby Mathew 1793fc4124cSDan Handley /* Get the mpidr for this cpu */ 1803fc4124cSDan Handley mpidr = read_mpidr_el1(); 1813fc4124cSDan Handley 1823fc4124cSDan Handley /* Program the jump address for the this cpu */ 183*38dce70fSSoby Mathew fvp_program_mailbox(mpidr, wakeup_address); 1843fc4124cSDan Handley 1853fc4124cSDan Handley /* Program the power controller to enable wakeup interrupts. */ 1863fc4124cSDan Handley fvp_pwrc_set_wen(mpidr); 1873fc4124cSDan Handley 1883fc4124cSDan Handley /* Perform the common cpu specific operations */ 1893fc4124cSDan Handley fvp_cpu_pwrdwn_common(); 1903fc4124cSDan Handley 1913fc4124cSDan Handley /* Perform the common cluster specific operations */ 192*38dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 193*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) 1943fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 1953fc4124cSDan Handley } 1963fc4124cSDan Handley 1973fc4124cSDan Handley /******************************************************************************* 198*38dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 199*38dce70fSSoby Mathew * being turned off earlier. The target_state encodes the low power state that 200*38dce70fSSoby Mathew * each level has woken up from. 2013fc4124cSDan Handley ******************************************************************************/ 202*38dce70fSSoby Mathew void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) 2033fc4124cSDan Handley { 2043fc4124cSDan Handley unsigned long mpidr; 2053fc4124cSDan Handley 206*38dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 207*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 2083fc4124cSDan Handley 2093fc4124cSDan Handley /* Get the mpidr for this cpu */ 2103fc4124cSDan Handley mpidr = read_mpidr_el1(); 2113fc4124cSDan Handley 2123fc4124cSDan Handley /* Perform the common cluster specific operations */ 213*38dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 214*38dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) { 2153fc4124cSDan Handley /* 2163fc4124cSDan Handley * This CPU might have woken up whilst the cluster was 2173fc4124cSDan Handley * attempting to power down. In this case the FVP power 2183fc4124cSDan Handley * controller will have a pending cluster power off request 2193fc4124cSDan Handley * which needs to be cleared by writing to the PPONR register. 2203fc4124cSDan Handley * This prevents the power controller from interpreting a 2213fc4124cSDan Handley * subsequent entry of this cpu into a simple wfi as a power 2223fc4124cSDan Handley * down request. 2233fc4124cSDan Handley */ 2243fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 2253fc4124cSDan Handley 2263fc4124cSDan Handley /* Enable coherency if this cluster was off */ 2273fc4124cSDan Handley fvp_cci_enable(); 2283fc4124cSDan Handley } 2293fc4124cSDan Handley 2303fc4124cSDan Handley /* 2313fc4124cSDan Handley * Clear PWKUPR.WEN bit to ensure interrupts do not interfere 2323fc4124cSDan Handley * with a cpu power down unless the bit is set again 2333fc4124cSDan Handley */ 2343fc4124cSDan Handley fvp_pwrc_clr_wen(mpidr); 2353fc4124cSDan Handley 2363fc4124cSDan Handley /* Zero the jump address in the mailbox for this cpu */ 2373fc4124cSDan Handley fvp_program_mailbox(mpidr, 0); 2383fc4124cSDan Handley 2393fc4124cSDan Handley /* Enable the gic cpu interface */ 2403fc4124cSDan Handley arm_gic_cpuif_setup(); 2413fc4124cSDan Handley 2423fc4124cSDan Handley /* TODO: This setup is needed only after a cold boot */ 2433fc4124cSDan Handley arm_gic_pcpu_distif_setup(); 2443fc4124cSDan Handley } 2453fc4124cSDan Handley 2463fc4124cSDan Handley /******************************************************************************* 247*38dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 248*38dce70fSSoby Mathew * having been suspended earlier. The target_state encodes the low power state 249*38dce70fSSoby Mathew * that each level has woken up from. 2503fc4124cSDan Handley * TODO: At the moment we reuse the on finisher and reinitialize the secure 2513fc4124cSDan Handley * context. Need to implement a separate suspend finisher. 2523fc4124cSDan Handley ******************************************************************************/ 253*38dce70fSSoby Mathew void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 2543fc4124cSDan Handley { 255*38dce70fSSoby Mathew /* 256*38dce70fSSoby Mathew * Nothing to be done on waking up from retention from CPU level. 257*38dce70fSSoby Mathew */ 258*38dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 259*38dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 260*38dce70fSSoby Mathew return; 261*38dce70fSSoby Mathew 262*38dce70fSSoby Mathew fvp_pwr_domain_on_finish(target_state); 2633fc4124cSDan Handley } 2643fc4124cSDan Handley 2653fc4124cSDan Handley /******************************************************************************* 2663fc4124cSDan Handley * FVP handlers to shutdown/reboot the system 2673fc4124cSDan Handley ******************************************************************************/ 2683fc4124cSDan Handley static void __dead2 fvp_system_off(void) 2693fc4124cSDan Handley { 2703fc4124cSDan Handley /* Write the System Configuration Control Register */ 2713fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2723fc4124cSDan Handley V2M_CFGCTRL_START | 2733fc4124cSDan Handley V2M_CFGCTRL_RW | 2743fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN)); 2753fc4124cSDan Handley wfi(); 2763fc4124cSDan Handley ERROR("FVP System Off: operation not handled.\n"); 2773fc4124cSDan Handley panic(); 2783fc4124cSDan Handley } 2793fc4124cSDan Handley 2803fc4124cSDan Handley static void __dead2 fvp_system_reset(void) 2813fc4124cSDan Handley { 2823fc4124cSDan Handley /* Write the System Configuration Control Register */ 2833fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2843fc4124cSDan Handley V2M_CFGCTRL_START | 2853fc4124cSDan Handley V2M_CFGCTRL_RW | 2863fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT)); 2873fc4124cSDan Handley wfi(); 2883fc4124cSDan Handley ERROR("FVP System Reset: operation not handled.\n"); 2893fc4124cSDan Handley panic(); 2903fc4124cSDan Handley } 2913fc4124cSDan Handley 2923fc4124cSDan Handley /******************************************************************************* 2933fc4124cSDan Handley * Export the platform handlers to enable psci to invoke them 2943fc4124cSDan Handley ******************************************************************************/ 295*38dce70fSSoby Mathew static const plat_psci_ops_t fvp_plat_psci_ops = { 296*38dce70fSSoby Mathew .cpu_standby = fvp_cpu_standby, 297*38dce70fSSoby Mathew .pwr_domain_on = fvp_pwr_domain_on, 298*38dce70fSSoby Mathew .pwr_domain_off = fvp_pwr_domain_off, 299*38dce70fSSoby Mathew .pwr_domain_suspend = fvp_pwr_domain_suspend, 300*38dce70fSSoby Mathew .pwr_domain_on_finish = fvp_pwr_domain_on_finish, 301*38dce70fSSoby Mathew .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, 3023fc4124cSDan Handley .system_off = fvp_system_off, 3033fc4124cSDan Handley .system_reset = fvp_system_reset, 3043fc4124cSDan Handley .validate_power_state = arm_validate_power_state 3053fc4124cSDan Handley }; 3063fc4124cSDan Handley 3073fc4124cSDan Handley /******************************************************************************* 308*38dce70fSSoby Mathew * Export the platform specific psci ops & initialize the fvp power controller 3093fc4124cSDan Handley ******************************************************************************/ 310*38dce70fSSoby Mathew int plat_setup_psci_ops(uintptr_t sec_entrypoint, 311*38dce70fSSoby Mathew const plat_psci_ops_t **psci_ops) 3123fc4124cSDan Handley { 313*38dce70fSSoby Mathew *psci_ops = &fvp_plat_psci_ops; 314*38dce70fSSoby Mathew wakeup_address = sec_entrypoint; 315*38dce70fSSoby Mathew 316*38dce70fSSoby Mathew flush_dcache_range((unsigned long)&wakeup_address, 317*38dce70fSSoby Mathew sizeof(wakeup_address)); 3183fc4124cSDan Handley return 0; 3193fc4124cSDan Handley } 320