1*3fc4124cSDan Handley /* 2*3fc4124cSDan Handley * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. 3*3fc4124cSDan Handley * 4*3fc4124cSDan Handley * Redistribution and use in source and binary forms, with or without 5*3fc4124cSDan Handley * modification, are permitted provided that the following conditions are met: 6*3fc4124cSDan Handley * 7*3fc4124cSDan Handley * Redistributions of source code must retain the above copyright notice, this 8*3fc4124cSDan Handley * list of conditions and the following disclaimer. 9*3fc4124cSDan Handley * 10*3fc4124cSDan Handley * Redistributions in binary form must reproduce the above copyright notice, 11*3fc4124cSDan Handley * this list of conditions and the following disclaimer in the documentation 12*3fc4124cSDan Handley * and/or other materials provided with the distribution. 13*3fc4124cSDan Handley * 14*3fc4124cSDan Handley * Neither the name of ARM nor the names of its contributors may be used 15*3fc4124cSDan Handley * to endorse or promote products derived from this software without specific 16*3fc4124cSDan Handley * prior written permission. 17*3fc4124cSDan Handley * 18*3fc4124cSDan Handley * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19*3fc4124cSDan Handley * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*3fc4124cSDan Handley * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*3fc4124cSDan Handley * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22*3fc4124cSDan Handley * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23*3fc4124cSDan Handley * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24*3fc4124cSDan Handley * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25*3fc4124cSDan Handley * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26*3fc4124cSDan Handley * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27*3fc4124cSDan Handley * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28*3fc4124cSDan Handley * POSSIBILITY OF SUCH DAMAGE. 29*3fc4124cSDan Handley */ 30*3fc4124cSDan Handley 31*3fc4124cSDan Handley #include <arch_helpers.h> 32*3fc4124cSDan Handley #include <arm_config.h> 33*3fc4124cSDan Handley #include <arm_gic.h> 34*3fc4124cSDan Handley #include <assert.h> 35*3fc4124cSDan Handley #include <debug.h> 36*3fc4124cSDan Handley #include <errno.h> 37*3fc4124cSDan Handley #include <mmio.h> 38*3fc4124cSDan Handley #include <platform.h> 39*3fc4124cSDan Handley #include <plat_arm.h> 40*3fc4124cSDan Handley #include <psci.h> 41*3fc4124cSDan Handley #include <v2m_def.h> 42*3fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h" 43*3fc4124cSDan Handley #include "fvp_def.h" 44*3fc4124cSDan Handley #include "fvp_private.h" 45*3fc4124cSDan Handley 46*3fc4124cSDan Handley 47*3fc4124cSDan Handley typedef volatile struct mailbox { 48*3fc4124cSDan Handley unsigned long value __aligned(CACHE_WRITEBACK_GRANULE); 49*3fc4124cSDan Handley } mailbox_t; 50*3fc4124cSDan Handley 51*3fc4124cSDan Handley /******************************************************************************* 52*3fc4124cSDan Handley * Private FVP function to program the mailbox for a cpu before it is released 53*3fc4124cSDan Handley * from reset. 54*3fc4124cSDan Handley ******************************************************************************/ 55*3fc4124cSDan Handley static void fvp_program_mailbox(uint64_t mpidr, uint64_t address) 56*3fc4124cSDan Handley { 57*3fc4124cSDan Handley uint64_t linear_id; 58*3fc4124cSDan Handley mailbox_t *fvp_mboxes; 59*3fc4124cSDan Handley 60*3fc4124cSDan Handley linear_id = platform_get_core_pos(mpidr); 61*3fc4124cSDan Handley fvp_mboxes = (mailbox_t *)MBOX_BASE; 62*3fc4124cSDan Handley fvp_mboxes[linear_id].value = address; 63*3fc4124cSDan Handley flush_dcache_range((unsigned long) &fvp_mboxes[linear_id], 64*3fc4124cSDan Handley sizeof(unsigned long)); 65*3fc4124cSDan Handley } 66*3fc4124cSDan Handley 67*3fc4124cSDan Handley /******************************************************************************* 68*3fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 69*3fc4124cSDan Handley * cpu in response to a CPU_OFF or CPU_SUSPEND request. 70*3fc4124cSDan Handley ******************************************************************************/ 71*3fc4124cSDan Handley static void fvp_cpu_pwrdwn_common(void) 72*3fc4124cSDan Handley { 73*3fc4124cSDan Handley /* Prevent interrupts from spuriously waking up this cpu */ 74*3fc4124cSDan Handley arm_gic_cpuif_deactivate(); 75*3fc4124cSDan Handley 76*3fc4124cSDan Handley /* Program the power controller to power off this cpu. */ 77*3fc4124cSDan Handley fvp_pwrc_write_ppoffr(read_mpidr_el1()); 78*3fc4124cSDan Handley } 79*3fc4124cSDan Handley 80*3fc4124cSDan Handley /******************************************************************************* 81*3fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 82*3fc4124cSDan Handley * cluster in response to a CPU_OFF or CPU_SUSPEND request. 83*3fc4124cSDan Handley ******************************************************************************/ 84*3fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void) 85*3fc4124cSDan Handley { 86*3fc4124cSDan Handley uint64_t mpidr = read_mpidr_el1(); 87*3fc4124cSDan Handley 88*3fc4124cSDan Handley /* Disable coherency if this cluster is to be turned off */ 89*3fc4124cSDan Handley fvp_cci_disable(); 90*3fc4124cSDan Handley 91*3fc4124cSDan Handley /* Program the power controller to turn the cluster off */ 92*3fc4124cSDan Handley fvp_pwrc_write_pcoffr(mpidr); 93*3fc4124cSDan Handley } 94*3fc4124cSDan Handley 95*3fc4124cSDan Handley /******************************************************************************* 96*3fc4124cSDan Handley * FVP handler called when an affinity instance is about to enter standby. 97*3fc4124cSDan Handley ******************************************************************************/ 98*3fc4124cSDan Handley void fvp_affinst_standby(unsigned int power_state) 99*3fc4124cSDan Handley { 100*3fc4124cSDan Handley /* 101*3fc4124cSDan Handley * Enter standby state 102*3fc4124cSDan Handley * dsb is good practice before using wfi to enter low power states 103*3fc4124cSDan Handley */ 104*3fc4124cSDan Handley dsb(); 105*3fc4124cSDan Handley wfi(); 106*3fc4124cSDan Handley } 107*3fc4124cSDan Handley 108*3fc4124cSDan Handley /******************************************************************************* 109*3fc4124cSDan Handley * FVP handler called when an affinity instance is about to be turned on. The 110*3fc4124cSDan Handley * level and mpidr determine the affinity instance. 111*3fc4124cSDan Handley ******************************************************************************/ 112*3fc4124cSDan Handley int fvp_affinst_on(unsigned long mpidr, 113*3fc4124cSDan Handley unsigned long sec_entrypoint, 114*3fc4124cSDan Handley unsigned int afflvl, 115*3fc4124cSDan Handley unsigned int state) 116*3fc4124cSDan Handley { 117*3fc4124cSDan Handley int rc = PSCI_E_SUCCESS; 118*3fc4124cSDan Handley unsigned int psysr; 119*3fc4124cSDan Handley 120*3fc4124cSDan Handley /* 121*3fc4124cSDan Handley * It's possible to turn on only affinity level 0 i.e. a cpu 122*3fc4124cSDan Handley * on the FVP. Ignore any other affinity level. 123*3fc4124cSDan Handley */ 124*3fc4124cSDan Handley if (afflvl != MPIDR_AFFLVL0) 125*3fc4124cSDan Handley return rc; 126*3fc4124cSDan Handley 127*3fc4124cSDan Handley /* 128*3fc4124cSDan Handley * Ensure that we do not cancel an inflight power off request 129*3fc4124cSDan Handley * for the target cpu. That would leave it in a zombie wfi. 130*3fc4124cSDan Handley * Wait for it to power off, program the jump address for the 131*3fc4124cSDan Handley * target cpu and then program the power controller to turn 132*3fc4124cSDan Handley * that cpu on 133*3fc4124cSDan Handley */ 134*3fc4124cSDan Handley do { 135*3fc4124cSDan Handley psysr = fvp_pwrc_read_psysr(mpidr); 136*3fc4124cSDan Handley } while (psysr & PSYSR_AFF_L0); 137*3fc4124cSDan Handley 138*3fc4124cSDan Handley fvp_program_mailbox(mpidr, sec_entrypoint); 139*3fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 140*3fc4124cSDan Handley 141*3fc4124cSDan Handley return rc; 142*3fc4124cSDan Handley } 143*3fc4124cSDan Handley 144*3fc4124cSDan Handley /******************************************************************************* 145*3fc4124cSDan Handley * FVP handler called when an affinity instance is about to be turned off. The 146*3fc4124cSDan Handley * level and mpidr determine the affinity instance. The 'state' arg. allows the 147*3fc4124cSDan Handley * platform to decide whether the cluster is being turned off and take apt 148*3fc4124cSDan Handley * actions. 149*3fc4124cSDan Handley * 150*3fc4124cSDan Handley * CAUTION: There is no guarantee that caches will remain turned on across calls 151*3fc4124cSDan Handley * to this function as each affinity level is dealt with. So do not write & read 152*3fc4124cSDan Handley * global variables across calls. It will be wise to do flush a write to the 153*3fc4124cSDan Handley * global to prevent unpredictable results. 154*3fc4124cSDan Handley ******************************************************************************/ 155*3fc4124cSDan Handley void fvp_affinst_off(unsigned int afflvl, 156*3fc4124cSDan Handley unsigned int state) 157*3fc4124cSDan Handley { 158*3fc4124cSDan Handley /* Determine if any platform actions need to be executed */ 159*3fc4124cSDan Handley if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) 160*3fc4124cSDan Handley return; 161*3fc4124cSDan Handley 162*3fc4124cSDan Handley /* 163*3fc4124cSDan Handley * If execution reaches this stage then this affinity level will be 164*3fc4124cSDan Handley * suspended. Perform at least the cpu specific actions followed the 165*3fc4124cSDan Handley * cluster specific operations if applicable. 166*3fc4124cSDan Handley */ 167*3fc4124cSDan Handley fvp_cpu_pwrdwn_common(); 168*3fc4124cSDan Handley 169*3fc4124cSDan Handley if (afflvl != MPIDR_AFFLVL0) 170*3fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 171*3fc4124cSDan Handley 172*3fc4124cSDan Handley } 173*3fc4124cSDan Handley 174*3fc4124cSDan Handley /******************************************************************************* 175*3fc4124cSDan Handley * FVP handler called when an affinity instance is about to be suspended. The 176*3fc4124cSDan Handley * level and mpidr determine the affinity instance. The 'state' arg. allows the 177*3fc4124cSDan Handley * platform to decide whether the cluster is being turned off and take apt 178*3fc4124cSDan Handley * actions. 179*3fc4124cSDan Handley * 180*3fc4124cSDan Handley * CAUTION: There is no guarantee that caches will remain turned on across calls 181*3fc4124cSDan Handley * to this function as each affinity level is dealt with. So do not write & read 182*3fc4124cSDan Handley * global variables across calls. It will be wise to do flush a write to the 183*3fc4124cSDan Handley * global to prevent unpredictable results. 184*3fc4124cSDan Handley ******************************************************************************/ 185*3fc4124cSDan Handley void fvp_affinst_suspend(unsigned long sec_entrypoint, 186*3fc4124cSDan Handley unsigned int afflvl, 187*3fc4124cSDan Handley unsigned int state) 188*3fc4124cSDan Handley { 189*3fc4124cSDan Handley unsigned long mpidr; 190*3fc4124cSDan Handley 191*3fc4124cSDan Handley /* Determine if any platform actions need to be executed. */ 192*3fc4124cSDan Handley if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) 193*3fc4124cSDan Handley return; 194*3fc4124cSDan Handley 195*3fc4124cSDan Handley /* Get the mpidr for this cpu */ 196*3fc4124cSDan Handley mpidr = read_mpidr_el1(); 197*3fc4124cSDan Handley 198*3fc4124cSDan Handley /* Program the jump address for the this cpu */ 199*3fc4124cSDan Handley fvp_program_mailbox(mpidr, sec_entrypoint); 200*3fc4124cSDan Handley 201*3fc4124cSDan Handley /* Program the power controller to enable wakeup interrupts. */ 202*3fc4124cSDan Handley fvp_pwrc_set_wen(mpidr); 203*3fc4124cSDan Handley 204*3fc4124cSDan Handley /* Perform the common cpu specific operations */ 205*3fc4124cSDan Handley fvp_cpu_pwrdwn_common(); 206*3fc4124cSDan Handley 207*3fc4124cSDan Handley /* Perform the common cluster specific operations */ 208*3fc4124cSDan Handley if (afflvl != MPIDR_AFFLVL0) 209*3fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 210*3fc4124cSDan Handley } 211*3fc4124cSDan Handley 212*3fc4124cSDan Handley /******************************************************************************* 213*3fc4124cSDan Handley * FVP handler called when an affinity instance has just been powered on after 214*3fc4124cSDan Handley * being turned off earlier. The level and mpidr determine the affinity 215*3fc4124cSDan Handley * instance. The 'state' arg. allows the platform to decide whether the cluster 216*3fc4124cSDan Handley * was turned off prior to wakeup and do what's necessary to setup it up 217*3fc4124cSDan Handley * correctly. 218*3fc4124cSDan Handley ******************************************************************************/ 219*3fc4124cSDan Handley void fvp_affinst_on_finish(unsigned int afflvl, 220*3fc4124cSDan Handley unsigned int state) 221*3fc4124cSDan Handley { 222*3fc4124cSDan Handley unsigned long mpidr; 223*3fc4124cSDan Handley 224*3fc4124cSDan Handley /* Determine if any platform actions need to be executed. */ 225*3fc4124cSDan Handley if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) 226*3fc4124cSDan Handley return; 227*3fc4124cSDan Handley 228*3fc4124cSDan Handley /* Get the mpidr for this cpu */ 229*3fc4124cSDan Handley mpidr = read_mpidr_el1(); 230*3fc4124cSDan Handley 231*3fc4124cSDan Handley /* Perform the common cluster specific operations */ 232*3fc4124cSDan Handley if (afflvl != MPIDR_AFFLVL0) { 233*3fc4124cSDan Handley /* 234*3fc4124cSDan Handley * This CPU might have woken up whilst the cluster was 235*3fc4124cSDan Handley * attempting to power down. In this case the FVP power 236*3fc4124cSDan Handley * controller will have a pending cluster power off request 237*3fc4124cSDan Handley * which needs to be cleared by writing to the PPONR register. 238*3fc4124cSDan Handley * This prevents the power controller from interpreting a 239*3fc4124cSDan Handley * subsequent entry of this cpu into a simple wfi as a power 240*3fc4124cSDan Handley * down request. 241*3fc4124cSDan Handley */ 242*3fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 243*3fc4124cSDan Handley 244*3fc4124cSDan Handley /* Enable coherency if this cluster was off */ 245*3fc4124cSDan Handley fvp_cci_enable(); 246*3fc4124cSDan Handley } 247*3fc4124cSDan Handley 248*3fc4124cSDan Handley /* 249*3fc4124cSDan Handley * Clear PWKUPR.WEN bit to ensure interrupts do not interfere 250*3fc4124cSDan Handley * with a cpu power down unless the bit is set again 251*3fc4124cSDan Handley */ 252*3fc4124cSDan Handley fvp_pwrc_clr_wen(mpidr); 253*3fc4124cSDan Handley 254*3fc4124cSDan Handley /* Zero the jump address in the mailbox for this cpu */ 255*3fc4124cSDan Handley fvp_program_mailbox(mpidr, 0); 256*3fc4124cSDan Handley 257*3fc4124cSDan Handley /* Enable the gic cpu interface */ 258*3fc4124cSDan Handley arm_gic_cpuif_setup(); 259*3fc4124cSDan Handley 260*3fc4124cSDan Handley /* TODO: This setup is needed only after a cold boot */ 261*3fc4124cSDan Handley arm_gic_pcpu_distif_setup(); 262*3fc4124cSDan Handley } 263*3fc4124cSDan Handley 264*3fc4124cSDan Handley /******************************************************************************* 265*3fc4124cSDan Handley * FVP handler called when an affinity instance has just been powered on after 266*3fc4124cSDan Handley * having been suspended earlier. The level and mpidr determine the affinity 267*3fc4124cSDan Handley * instance. 268*3fc4124cSDan Handley * TODO: At the moment we reuse the on finisher and reinitialize the secure 269*3fc4124cSDan Handley * context. Need to implement a separate suspend finisher. 270*3fc4124cSDan Handley ******************************************************************************/ 271*3fc4124cSDan Handley void fvp_affinst_suspend_finish(unsigned int afflvl, 272*3fc4124cSDan Handley unsigned int state) 273*3fc4124cSDan Handley { 274*3fc4124cSDan Handley fvp_affinst_on_finish(afflvl, state); 275*3fc4124cSDan Handley } 276*3fc4124cSDan Handley 277*3fc4124cSDan Handley /******************************************************************************* 278*3fc4124cSDan Handley * FVP handlers to shutdown/reboot the system 279*3fc4124cSDan Handley ******************************************************************************/ 280*3fc4124cSDan Handley static void __dead2 fvp_system_off(void) 281*3fc4124cSDan Handley { 282*3fc4124cSDan Handley /* Write the System Configuration Control Register */ 283*3fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 284*3fc4124cSDan Handley V2M_CFGCTRL_START | 285*3fc4124cSDan Handley V2M_CFGCTRL_RW | 286*3fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN)); 287*3fc4124cSDan Handley wfi(); 288*3fc4124cSDan Handley ERROR("FVP System Off: operation not handled.\n"); 289*3fc4124cSDan Handley panic(); 290*3fc4124cSDan Handley } 291*3fc4124cSDan Handley 292*3fc4124cSDan Handley static void __dead2 fvp_system_reset(void) 293*3fc4124cSDan Handley { 294*3fc4124cSDan Handley /* Write the System Configuration Control Register */ 295*3fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 296*3fc4124cSDan Handley V2M_CFGCTRL_START | 297*3fc4124cSDan Handley V2M_CFGCTRL_RW | 298*3fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT)); 299*3fc4124cSDan Handley wfi(); 300*3fc4124cSDan Handley ERROR("FVP System Reset: operation not handled.\n"); 301*3fc4124cSDan Handley panic(); 302*3fc4124cSDan Handley } 303*3fc4124cSDan Handley 304*3fc4124cSDan Handley /******************************************************************************* 305*3fc4124cSDan Handley * Export the platform handlers to enable psci to invoke them 306*3fc4124cSDan Handley ******************************************************************************/ 307*3fc4124cSDan Handley static const plat_pm_ops_t fvp_plat_pm_ops = { 308*3fc4124cSDan Handley .affinst_standby = fvp_affinst_standby, 309*3fc4124cSDan Handley .affinst_on = fvp_affinst_on, 310*3fc4124cSDan Handley .affinst_off = fvp_affinst_off, 311*3fc4124cSDan Handley .affinst_suspend = fvp_affinst_suspend, 312*3fc4124cSDan Handley .affinst_on_finish = fvp_affinst_on_finish, 313*3fc4124cSDan Handley .affinst_suspend_finish = fvp_affinst_suspend_finish, 314*3fc4124cSDan Handley .system_off = fvp_system_off, 315*3fc4124cSDan Handley .system_reset = fvp_system_reset, 316*3fc4124cSDan Handley .validate_power_state = arm_validate_power_state 317*3fc4124cSDan Handley }; 318*3fc4124cSDan Handley 319*3fc4124cSDan Handley /******************************************************************************* 320*3fc4124cSDan Handley * Export the platform specific power ops & initialize the fvp power controller 321*3fc4124cSDan Handley ******************************************************************************/ 322*3fc4124cSDan Handley int platform_setup_pm(const plat_pm_ops_t **plat_ops) 323*3fc4124cSDan Handley { 324*3fc4124cSDan Handley *plat_ops = &fvp_plat_pm_ops; 325*3fc4124cSDan Handley return 0; 326*3fc4124cSDan Handley } 327