1*6fba6e04STony Xie /* 2*6fba6e04STony Xie * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 3*6fba6e04STony Xie * 4*6fba6e04STony Xie * Redistribution and use in source and binary forms, with or without 5*6fba6e04STony Xie * modification, are permitted provided that the following conditions are met: 6*6fba6e04STony Xie * 7*6fba6e04STony Xie * Redistributions of source code must retain the above copyright notice, this 8*6fba6e04STony Xie * list of conditions and the following disclaimer. 9*6fba6e04STony Xie * 10*6fba6e04STony Xie * Redistributions in binary form must reproduce the above copyright notice, 11*6fba6e04STony Xie * this list of conditions and the following disclaimer in the documentation 12*6fba6e04STony Xie * and/or other materials provided with the distribution. 13*6fba6e04STony Xie * 14*6fba6e04STony Xie * Neither the name of ARM nor the names of its contributors may be used 15*6fba6e04STony Xie * to endorse or promote products derived from this software without specific 16*6fba6e04STony Xie * prior written permission. 17*6fba6e04STony Xie * 18*6fba6e04STony Xie * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19*6fba6e04STony Xie * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*6fba6e04STony Xie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*6fba6e04STony Xie * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22*6fba6e04STony Xie * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23*6fba6e04STony Xie * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24*6fba6e04STony Xie * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25*6fba6e04STony Xie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26*6fba6e04STony Xie * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27*6fba6e04STony Xie * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28*6fba6e04STony Xie * POSSIBILITY OF SUCH DAMAGE. 29*6fba6e04STony Xie */ 30*6fba6e04STony Xie 31*6fba6e04STony Xie #include <arch_helpers.h> 32*6fba6e04STony Xie #include <assert.h> 33*6fba6e04STony Xie #include <console.h> 34*6fba6e04STony Xie #include <errno.h> 35*6fba6e04STony Xie #include <debug.h> 36*6fba6e04STony Xie #include <psci.h> 37*6fba6e04STony Xie #include <delay_timer.h> 38*6fba6e04STony Xie #include <platform_def.h> 39*6fba6e04STony Xie #include <plat_private.h> 40*6fba6e04STony Xie 41*6fba6e04STony Xie /* Macros to read the rk power domain state */ 42*6fba6e04STony Xie #define RK_CORE_PWR_STATE(state) \ 43*6fba6e04STony Xie ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 44*6fba6e04STony Xie #define RK_CLUSTER_PWR_STATE(state) \ 45*6fba6e04STony Xie ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 46*6fba6e04STony Xie #define RK_SYSTEM_PWR_STATE(state) \ 47*6fba6e04STony Xie ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 48*6fba6e04STony Xie 49*6fba6e04STony Xie static uintptr_t rockchip_sec_entrypoint; 50*6fba6e04STony Xie 51*6fba6e04STony Xie static struct rockchip_pm_ops_cb *rockchip_ops; 52*6fba6e04STony Xie 53*6fba6e04STony Xie static void plat_rockchip_sys_pwr_domain_resume(void) 54*6fba6e04STony Xie { 55*6fba6e04STony Xie plat_rockchip_gic_init(); 56*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->sys_pwr_dm_resume) 57*6fba6e04STony Xie rockchip_ops->sys_pwr_dm_resume(); 58*6fba6e04STony Xie } 59*6fba6e04STony Xie 60*6fba6e04STony Xie static void plat_rockchip_cores_pwr_domain_resume(void) 61*6fba6e04STony Xie { 62*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->cores_pwr_dm_resume) 63*6fba6e04STony Xie rockchip_ops->cores_pwr_dm_resume(); 64*6fba6e04STony Xie 65*6fba6e04STony Xie /* Enable the gic cpu interface */ 66*6fba6e04STony Xie plat_rockchip_gic_pcpu_init(); 67*6fba6e04STony Xie /* Program the gic per-cpu distributor or re-distributor interface */ 68*6fba6e04STony Xie plat_rockchip_gic_cpuif_enable(); 69*6fba6e04STony Xie } 70*6fba6e04STony Xie 71*6fba6e04STony Xie /******************************************************************************* 72*6fba6e04STony Xie * Rockchip standard platform handler called to check the validity of the power 73*6fba6e04STony Xie * state parameter. 74*6fba6e04STony Xie ******************************************************************************/ 75*6fba6e04STony Xie int rockchip_validate_power_state(unsigned int power_state, 76*6fba6e04STony Xie psci_power_state_t *req_state) 77*6fba6e04STony Xie { 78*6fba6e04STony Xie int pstate = psci_get_pstate_type(power_state); 79*6fba6e04STony Xie int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 80*6fba6e04STony Xie int i; 81*6fba6e04STony Xie 82*6fba6e04STony Xie assert(req_state); 83*6fba6e04STony Xie 84*6fba6e04STony Xie if (pwr_lvl > PLAT_MAX_PWR_LVL) 85*6fba6e04STony Xie return PSCI_E_INVALID_PARAMS; 86*6fba6e04STony Xie 87*6fba6e04STony Xie /* Sanity check the requested state */ 88*6fba6e04STony Xie if (pstate == PSTATE_TYPE_STANDBY) { 89*6fba6e04STony Xie /* 90*6fba6e04STony Xie * It's probably to enter standby only on power level 0 91*6fba6e04STony Xie * ignore any other power level. 92*6fba6e04STony Xie */ 93*6fba6e04STony Xie if (pwr_lvl != MPIDR_AFFLVL0) 94*6fba6e04STony Xie return PSCI_E_INVALID_PARAMS; 95*6fba6e04STony Xie 96*6fba6e04STony Xie req_state->pwr_domain_state[MPIDR_AFFLVL0] = 97*6fba6e04STony Xie PLAT_MAX_RET_STATE; 98*6fba6e04STony Xie } else { 99*6fba6e04STony Xie for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 100*6fba6e04STony Xie req_state->pwr_domain_state[i] = 101*6fba6e04STony Xie PLAT_MAX_OFF_STATE; 102*6fba6e04STony Xie } 103*6fba6e04STony Xie 104*6fba6e04STony Xie /* We expect the 'state id' to be zero */ 105*6fba6e04STony Xie if (psci_get_pstate_id(power_state)) 106*6fba6e04STony Xie return PSCI_E_INVALID_PARAMS; 107*6fba6e04STony Xie 108*6fba6e04STony Xie return PSCI_E_SUCCESS; 109*6fba6e04STony Xie } 110*6fba6e04STony Xie 111*6fba6e04STony Xie void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state) 112*6fba6e04STony Xie { 113*6fba6e04STony Xie int i; 114*6fba6e04STony Xie 115*6fba6e04STony Xie for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 116*6fba6e04STony Xie req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 117*6fba6e04STony Xie } 118*6fba6e04STony Xie 119*6fba6e04STony Xie /******************************************************************************* 120*6fba6e04STony Xie * RockChip handler called when a CPU is about to enter standby. 121*6fba6e04STony Xie ******************************************************************************/ 122*6fba6e04STony Xie void rockchip_cpu_standby(plat_local_state_t cpu_state) 123*6fba6e04STony Xie { 124*6fba6e04STony Xie unsigned int scr; 125*6fba6e04STony Xie 126*6fba6e04STony Xie assert(cpu_state == PLAT_MAX_RET_STATE); 127*6fba6e04STony Xie 128*6fba6e04STony Xie scr = read_scr_el3(); 129*6fba6e04STony Xie /* Enable PhysicalIRQ bit for NS world to wake the CPU */ 130*6fba6e04STony Xie write_scr_el3(scr | SCR_IRQ_BIT); 131*6fba6e04STony Xie isb(); 132*6fba6e04STony Xie dsb(); 133*6fba6e04STony Xie wfi(); 134*6fba6e04STony Xie 135*6fba6e04STony Xie /* 136*6fba6e04STony Xie * Restore SCR to the original value, synchronisation of scr_el3 is 137*6fba6e04STony Xie * done by eret while el3_exit to save some execution cycles. 138*6fba6e04STony Xie */ 139*6fba6e04STony Xie write_scr_el3(scr); 140*6fba6e04STony Xie } 141*6fba6e04STony Xie 142*6fba6e04STony Xie /******************************************************************************* 143*6fba6e04STony Xie * RockChip handler called when a power domain is about to be turned on. The 144*6fba6e04STony Xie * mpidr determines the CPU to be turned on. 145*6fba6e04STony Xie ******************************************************************************/ 146*6fba6e04STony Xie int rockchip_pwr_domain_on(u_register_t mpidr) 147*6fba6e04STony Xie { 148*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->cores_pwr_dm_on) 149*6fba6e04STony Xie rockchip_ops->cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint); 150*6fba6e04STony Xie 151*6fba6e04STony Xie return PSCI_E_SUCCESS; 152*6fba6e04STony Xie } 153*6fba6e04STony Xie 154*6fba6e04STony Xie /******************************************************************************* 155*6fba6e04STony Xie * RockChip handler called when a power domain is about to be turned off. The 156*6fba6e04STony Xie * target_state encodes the power state that each level should transition to. 157*6fba6e04STony Xie ******************************************************************************/ 158*6fba6e04STony Xie void rockchip_pwr_domain_off(const psci_power_state_t *target_state) 159*6fba6e04STony Xie { 160*6fba6e04STony Xie assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE); 161*6fba6e04STony Xie 162*6fba6e04STony Xie plat_rockchip_gic_cpuif_disable(); 163*6fba6e04STony Xie 164*6fba6e04STony Xie if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 165*6fba6e04STony Xie plat_cci_disable(); 166*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->cores_pwr_dm_off) 167*6fba6e04STony Xie rockchip_ops->cores_pwr_dm_off(); 168*6fba6e04STony Xie } 169*6fba6e04STony Xie 170*6fba6e04STony Xie /******************************************************************************* 171*6fba6e04STony Xie * RockChip handler called when a power domain is about to be suspended. The 172*6fba6e04STony Xie * target_state encodes the power state that each level should transition to. 173*6fba6e04STony Xie ******************************************************************************/ 174*6fba6e04STony Xie void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state) 175*6fba6e04STony Xie { 176*6fba6e04STony Xie if (RK_CORE_PWR_STATE(target_state) == PLAT_MAX_RET_STATE) 177*6fba6e04STony Xie return; 178*6fba6e04STony Xie 179*6fba6e04STony Xie assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE); 180*6fba6e04STony Xie 181*6fba6e04STony Xie if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 182*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->sys_pwr_dm_suspend) 183*6fba6e04STony Xie rockchip_ops->sys_pwr_dm_suspend(); 184*6fba6e04STony Xie } else { 185*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->cores_pwr_dm_suspend) 186*6fba6e04STony Xie rockchip_ops->cores_pwr_dm_suspend(); 187*6fba6e04STony Xie } 188*6fba6e04STony Xie 189*6fba6e04STony Xie /* Prevent interrupts from spuriously waking up this cpu */ 190*6fba6e04STony Xie plat_rockchip_gic_cpuif_disable(); 191*6fba6e04STony Xie 192*6fba6e04STony Xie /* Perform the common cluster specific operations */ 193*6fba6e04STony Xie if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 194*6fba6e04STony Xie plat_cci_disable(); 195*6fba6e04STony Xie } 196*6fba6e04STony Xie 197*6fba6e04STony Xie /******************************************************************************* 198*6fba6e04STony Xie * RockChip handler called when a power domain has just been powered on after 199*6fba6e04STony Xie * being turned off earlier. The target_state encodes the low power state that 200*6fba6e04STony Xie * each level has woken up from. 201*6fba6e04STony Xie ******************************************************************************/ 202*6fba6e04STony Xie void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state) 203*6fba6e04STony Xie { 204*6fba6e04STony Xie assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE); 205*6fba6e04STony Xie 206*6fba6e04STony Xie if (rockchip_ops && rockchip_ops->cores_pwr_dm_on_finish) 207*6fba6e04STony Xie rockchip_ops->cores_pwr_dm_on_finish(); 208*6fba6e04STony Xie 209*6fba6e04STony Xie /* Perform the common cluster specific operations */ 210*6fba6e04STony Xie if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 211*6fba6e04STony Xie /* Enable coherency if this cluster was off */ 212*6fba6e04STony Xie plat_cci_enable(); 213*6fba6e04STony Xie } 214*6fba6e04STony Xie 215*6fba6e04STony Xie /* Enable the gic cpu interface */ 216*6fba6e04STony Xie plat_rockchip_gic_pcpu_init(); 217*6fba6e04STony Xie 218*6fba6e04STony Xie /* Program the gic per-cpu distributor or re-distributor interface */ 219*6fba6e04STony Xie plat_rockchip_gic_cpuif_enable(); 220*6fba6e04STony Xie } 221*6fba6e04STony Xie 222*6fba6e04STony Xie /******************************************************************************* 223*6fba6e04STony Xie * RockChip handler called when a power domain has just been powered on after 224*6fba6e04STony Xie * having been suspended earlier. The target_state encodes the low power state 225*6fba6e04STony Xie * that each level has woken up from. 226*6fba6e04STony Xie * TODO: At the moment we reuse the on finisher and reinitialize the secure 227*6fba6e04STony Xie * context. Need to implement a separate suspend finisher. 228*6fba6e04STony Xie ******************************************************************************/ 229*6fba6e04STony Xie void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 230*6fba6e04STony Xie { 231*6fba6e04STony Xie /* Nothing to be done on waking up from retention from CPU level */ 232*6fba6e04STony Xie if (RK_CORE_PWR_STATE(target_state) == PLAT_MAX_RET_STATE) 233*6fba6e04STony Xie return; 234*6fba6e04STony Xie 235*6fba6e04STony Xie /* Perform system domain restore if woken up from system suspend */ 236*6fba6e04STony Xie if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 237*6fba6e04STony Xie plat_rockchip_sys_pwr_domain_resume(); 238*6fba6e04STony Xie else 239*6fba6e04STony Xie plat_rockchip_cores_pwr_domain_resume(); 240*6fba6e04STony Xie 241*6fba6e04STony Xie /* Perform the common cluster specific operations */ 242*6fba6e04STony Xie if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 243*6fba6e04STony Xie /* Enable coherency if this cluster was off */ 244*6fba6e04STony Xie plat_cci_enable(); 245*6fba6e04STony Xie } 246*6fba6e04STony Xie } 247*6fba6e04STony Xie 248*6fba6e04STony Xie /******************************************************************************* 249*6fba6e04STony Xie * RockChip handlers to reboot the system 250*6fba6e04STony Xie ******************************************************************************/ 251*6fba6e04STony Xie static void __dead2 rockchip_system_reset(void) 252*6fba6e04STony Xie { 253*6fba6e04STony Xie assert(rockchip_ops && rockchip_ops->sys_gbl_soft_reset); 254*6fba6e04STony Xie 255*6fba6e04STony Xie rockchip_ops->sys_gbl_soft_reset(); 256*6fba6e04STony Xie } 257*6fba6e04STony Xie 258*6fba6e04STony Xie /******************************************************************************* 259*6fba6e04STony Xie * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip 260*6fba6e04STony Xie * standard 261*6fba6e04STony Xie * platform layer will take care of registering the handlers with PSCI. 262*6fba6e04STony Xie ******************************************************************************/ 263*6fba6e04STony Xie const plat_psci_ops_t plat_rockchip_psci_pm_ops = { 264*6fba6e04STony Xie .cpu_standby = rockchip_cpu_standby, 265*6fba6e04STony Xie .pwr_domain_on = rockchip_pwr_domain_on, 266*6fba6e04STony Xie .pwr_domain_off = rockchip_pwr_domain_off, 267*6fba6e04STony Xie .pwr_domain_suspend = rockchip_pwr_domain_suspend, 268*6fba6e04STony Xie .pwr_domain_on_finish = rockchip_pwr_domain_on_finish, 269*6fba6e04STony Xie .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish, 270*6fba6e04STony Xie .system_reset = rockchip_system_reset, 271*6fba6e04STony Xie .validate_power_state = rockchip_validate_power_state, 272*6fba6e04STony Xie .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state 273*6fba6e04STony Xie }; 274*6fba6e04STony Xie 275*6fba6e04STony Xie int plat_setup_psci_ops(uintptr_t sec_entrypoint, 276*6fba6e04STony Xie const plat_psci_ops_t **psci_ops) 277*6fba6e04STony Xie { 278*6fba6e04STony Xie *psci_ops = &plat_rockchip_psci_pm_ops; 279*6fba6e04STony Xie rockchip_sec_entrypoint = sec_entrypoint; 280*6fba6e04STony Xie return 0; 281*6fba6e04STony Xie } 282*6fba6e04STony Xie 283*6fba6e04STony Xie void plat_setup_rockchip_pm_ops(struct rockchip_pm_ops_cb *ops) 284*6fba6e04STony Xie { 285*6fba6e04STony Xie rockchip_ops = ops; 286*6fba6e04STony Xie } 287