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