1*f180a3b7SHieu Nguyen /* 2*f180a3b7SHieu Nguyen * Copyright (c) 2025, Renesas Electronics Corporation. All rights reserved. 3*f180a3b7SHieu Nguyen * 4*f180a3b7SHieu Nguyen * SPDX-License-Identifier: BSD-3-Clause 5*f180a3b7SHieu Nguyen */ 6*f180a3b7SHieu Nguyen 7*f180a3b7SHieu Nguyen #include <errno.h> 8*f180a3b7SHieu Nguyen 9*f180a3b7SHieu Nguyen #include <arch_helpers.h> 10*f180a3b7SHieu Nguyen #include <common/bl_common.h> 11*f180a3b7SHieu Nguyen #include <common/debug.h> 12*f180a3b7SHieu Nguyen #include <drivers/arm/cci.h> 13*f180a3b7SHieu Nguyen #include <drivers/arm/gicv3.h> 14*f180a3b7SHieu Nguyen #include <lib/bakery_lock.h> 15*f180a3b7SHieu Nguyen #include <lib/mmio.h> 16*f180a3b7SHieu Nguyen #include <lib/psci/psci.h> 17*f180a3b7SHieu Nguyen #include <plat/common/platform.h> 18*f180a3b7SHieu Nguyen #include "pwrc.h" 19*f180a3b7SHieu Nguyen #include "timer.h" 20*f180a3b7SHieu Nguyen 21*f180a3b7SHieu Nguyen #include "platform_def.h" 22*f180a3b7SHieu Nguyen #include "rcar_def.h" 23*f180a3b7SHieu Nguyen #include "rcar_private.h" 24*f180a3b7SHieu Nguyen 25*f180a3b7SHieu Nguyen #define SYSTEM_PWR_STATE(s) ((s)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 26*f180a3b7SHieu Nguyen #define CORE_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL0]) 27*f180a3b7SHieu Nguyen 28*f180a3b7SHieu Nguyen static uintptr_t rcar_sec_entrypoint; 29*f180a3b7SHieu Nguyen static gicv3_redist_ctx_t rdist_ctx[PLATFORM_CORE_COUNT]; 30*f180a3b7SHieu Nguyen static gicv3_dist_ctx_t dist_ctx; 31*f180a3b7SHieu Nguyen 32*f180a3b7SHieu Nguyen static void rcar_program_mailbox(u_register_t mpidr, uintptr_t address) 33*f180a3b7SHieu Nguyen { 34*f180a3b7SHieu Nguyen uintptr_t range; 35*f180a3b7SHieu Nguyen mailbox_t *rcar_mboxes = (mailbox_t *) MBOX_BASE; 36*f180a3b7SHieu Nguyen int linear_id = plat_core_pos_by_mpidr(mpidr); 37*f180a3b7SHieu Nguyen 38*f180a3b7SHieu Nguyen if (linear_id < 0) { 39*f180a3b7SHieu Nguyen ERROR("BL3-1 : The value of passed MPIDR is invalid."); 40*f180a3b7SHieu Nguyen panic(); 41*f180a3b7SHieu Nguyen } 42*f180a3b7SHieu Nguyen rcar_mboxes[linear_id].value = address; 43*f180a3b7SHieu Nguyen range = (uintptr_t)(&rcar_mboxes[linear_id]); 44*f180a3b7SHieu Nguyen 45*f180a3b7SHieu Nguyen flush_dcache_range(range, sizeof(mailbox_t)); 46*f180a3b7SHieu Nguyen } 47*f180a3b7SHieu Nguyen 48*f180a3b7SHieu Nguyen static void rcar_cpu_standby(plat_local_state_t cpu_state) 49*f180a3b7SHieu Nguyen { 50*f180a3b7SHieu Nguyen u_register_t scr_el3 = read_scr_el3(); 51*f180a3b7SHieu Nguyen 52*f180a3b7SHieu Nguyen write_scr_el3(scr_el3 | SCR_IRQ_BIT); 53*f180a3b7SHieu Nguyen dsb(); 54*f180a3b7SHieu Nguyen wfi(); 55*f180a3b7SHieu Nguyen write_scr_el3(scr_el3); 56*f180a3b7SHieu Nguyen } 57*f180a3b7SHieu Nguyen 58*f180a3b7SHieu Nguyen static int rcar_pwr_domain_on(u_register_t mpidr) 59*f180a3b7SHieu Nguyen { 60*f180a3b7SHieu Nguyen rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 61*f180a3b7SHieu Nguyen rcar_scmi_cpuon(mpidr); 62*f180a3b7SHieu Nguyen 63*f180a3b7SHieu Nguyen return PSCI_E_SUCCESS; 64*f180a3b7SHieu Nguyen } 65*f180a3b7SHieu Nguyen 66*f180a3b7SHieu Nguyen static void rcar_pwr_domain_on_finish(const psci_power_state_t *target_state) 67*f180a3b7SHieu Nguyen { 68*f180a3b7SHieu Nguyen u_register_t mpidr = read_mpidr_el1(); 69*f180a3b7SHieu Nguyen 70*f180a3b7SHieu Nguyen rcar_pwrc_disable_interrupt_wakeup(mpidr); 71*f180a3b7SHieu Nguyen rcar_program_mailbox(mpidr, 0U); 72*f180a3b7SHieu Nguyen } 73*f180a3b7SHieu Nguyen 74*f180a3b7SHieu Nguyen static void rcar_pwr_domain_off(const psci_power_state_t *target_state) 75*f180a3b7SHieu Nguyen { 76*f180a3b7SHieu Nguyen u_register_t mpidr = read_mpidr_el1(); 77*f180a3b7SHieu Nguyen 78*f180a3b7SHieu Nguyen rcar_pwrc_disable_interrupt_wakeup(mpidr); 79*f180a3b7SHieu Nguyen 80*f180a3b7SHieu Nguyen rcar_scmi_cpuoff(target_state); 81*f180a3b7SHieu Nguyen } 82*f180a3b7SHieu Nguyen 83*f180a3b7SHieu Nguyen static void rcar_pwr_domain_suspend(const psci_power_state_t *target_state) 84*f180a3b7SHieu Nguyen { 85*f180a3b7SHieu Nguyen u_register_t mpidr = read_mpidr_el1(); 86*f180a3b7SHieu Nguyen 87*f180a3b7SHieu Nguyen if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) { 88*f180a3b7SHieu Nguyen return; 89*f180a3b7SHieu Nguyen } 90*f180a3b7SHieu Nguyen 91*f180a3b7SHieu Nguyen rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 92*f180a3b7SHieu Nguyen rcar_pwrc_enable_interrupt_wakeup(mpidr); 93*f180a3b7SHieu Nguyen 94*f180a3b7SHieu Nguyen if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 95*f180a3b7SHieu Nguyen for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) 96*f180a3b7SHieu Nguyen gicv3_rdistif_save(i, &rdist_ctx[i]); 97*f180a3b7SHieu Nguyen gicv3_distif_save(&dist_ctx); 98*f180a3b7SHieu Nguyen } 99*f180a3b7SHieu Nguyen } 100*f180a3b7SHieu Nguyen 101*f180a3b7SHieu Nguyen static void rcar_pwr_domain_suspend_finish(const psci_power_state_t 102*f180a3b7SHieu Nguyen *target_state) 103*f180a3b7SHieu Nguyen { 104*f180a3b7SHieu Nguyen u_register_t mpidr = read_mpidr_el1(); 105*f180a3b7SHieu Nguyen 106*f180a3b7SHieu Nguyen if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 107*f180a3b7SHieu Nguyen rcar_pwrc_restore_timer_state(); 108*f180a3b7SHieu Nguyen 109*f180a3b7SHieu Nguyen plat_rcar_scmi_setup(); 110*f180a3b7SHieu Nguyen } 111*f180a3b7SHieu Nguyen 112*f180a3b7SHieu Nguyen rcar_pwrc_disable_interrupt_wakeup(mpidr); 113*f180a3b7SHieu Nguyen rcar_program_mailbox(mpidr, 0U); 114*f180a3b7SHieu Nguyen if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 115*f180a3b7SHieu Nguyen gicv3_distif_init_restore(&dist_ctx); 116*f180a3b7SHieu Nguyen for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) 117*f180a3b7SHieu Nguyen gicv3_rdistif_init_restore(i, &rdist_ctx[i]); 118*f180a3b7SHieu Nguyen } 119*f180a3b7SHieu Nguyen } 120*f180a3b7SHieu Nguyen 121*f180a3b7SHieu Nguyen static void __dead2 rcar_system_off(void) 122*f180a3b7SHieu Nguyen { 123*f180a3b7SHieu Nguyen u_register_t mpidr = read_mpidr_el1(); 124*f180a3b7SHieu Nguyen uint32_t rtn_on; 125*f180a3b7SHieu Nguyen 126*f180a3b7SHieu Nguyen rtn_on = rcar_pwrc_cpu_on_check(mpidr); 127*f180a3b7SHieu Nguyen 128*f180a3b7SHieu Nguyen if (rtn_on > 0U) { 129*f180a3b7SHieu Nguyen panic(); 130*f180a3b7SHieu Nguyen } 131*f180a3b7SHieu Nguyen 132*f180a3b7SHieu Nguyen rcar_scmi_sys_shutdown(); 133*f180a3b7SHieu Nguyen 134*f180a3b7SHieu Nguyen wfi(); 135*f180a3b7SHieu Nguyen ERROR("RCAR System Off: operation not handled.\n"); 136*f180a3b7SHieu Nguyen panic(); 137*f180a3b7SHieu Nguyen } 138*f180a3b7SHieu Nguyen 139*f180a3b7SHieu Nguyen static void __dead2 rcar_system_reset(void) 140*f180a3b7SHieu Nguyen { 141*f180a3b7SHieu Nguyen rcar_scmi_sys_reboot(); 142*f180a3b7SHieu Nguyen 143*f180a3b7SHieu Nguyen wfi(); 144*f180a3b7SHieu Nguyen 145*f180a3b7SHieu Nguyen ERROR("RCAR System Reset: operation not handled.\n"); 146*f180a3b7SHieu Nguyen panic(); 147*f180a3b7SHieu Nguyen } 148*f180a3b7SHieu Nguyen 149*f180a3b7SHieu Nguyen static void __dead2 rcar_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 150*f180a3b7SHieu Nguyen { 151*f180a3b7SHieu Nguyen 152*f180a3b7SHieu Nguyen if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 153*f180a3b7SHieu Nguyen rcar_pwrc_suspend_to_ram(); 154*f180a3b7SHieu Nguyen } 155*f180a3b7SHieu Nguyen 156*f180a3b7SHieu Nguyen wfi(); 157*f180a3b7SHieu Nguyen 158*f180a3b7SHieu Nguyen ERROR("RCAR Power Down: operation not handled.\n"); 159*f180a3b7SHieu Nguyen panic(); 160*f180a3b7SHieu Nguyen } 161*f180a3b7SHieu Nguyen 162*f180a3b7SHieu Nguyen static int rcar_validate_power_state(unsigned int power_state, 163*f180a3b7SHieu Nguyen psci_power_state_t *req_state) 164*f180a3b7SHieu Nguyen { 165*f180a3b7SHieu Nguyen uint32_t pwr_lvl = psci_get_pstate_pwrlvl(power_state); 166*f180a3b7SHieu Nguyen uint32_t pstate = psci_get_pstate_type(power_state); 167*f180a3b7SHieu Nguyen uint64_t i; 168*f180a3b7SHieu Nguyen 169*f180a3b7SHieu Nguyen if (pstate == PSTATE_TYPE_STANDBY) { 170*f180a3b7SHieu Nguyen if (pwr_lvl != MPIDR_AFFLVL0) { 171*f180a3b7SHieu Nguyen return PSCI_E_INVALID_PARAMS; 172*f180a3b7SHieu Nguyen } 173*f180a3b7SHieu Nguyen 174*f180a3b7SHieu Nguyen req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 175*f180a3b7SHieu Nguyen } else { 176*f180a3b7SHieu Nguyen for (i = MPIDR_AFFLVL0; i <= (uint64_t)pwr_lvl; i++) { 177*f180a3b7SHieu Nguyen req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 178*f180a3b7SHieu Nguyen } 179*f180a3b7SHieu Nguyen } 180*f180a3b7SHieu Nguyen 181*f180a3b7SHieu Nguyen if (psci_get_pstate_id(power_state) != 0U) { 182*f180a3b7SHieu Nguyen return PSCI_E_INVALID_PARAMS; 183*f180a3b7SHieu Nguyen } 184*f180a3b7SHieu Nguyen 185*f180a3b7SHieu Nguyen return PSCI_E_SUCCESS; 186*f180a3b7SHieu Nguyen } 187*f180a3b7SHieu Nguyen 188*f180a3b7SHieu Nguyen static void rcar_get_sys_suspend_power_state(psci_power_state_t *req_state) 189*f180a3b7SHieu Nguyen { 190*f180a3b7SHieu Nguyen uint64_t i; 191*f180a3b7SHieu Nguyen 192*f180a3b7SHieu Nguyen for (i = MPIDR_AFFLVL0; i <= (uint64_t)PLAT_MAX_PWR_LVL; i++) { 193*f180a3b7SHieu Nguyen req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 194*f180a3b7SHieu Nguyen } 195*f180a3b7SHieu Nguyen } 196*f180a3b7SHieu Nguyen 197*f180a3b7SHieu Nguyen static plat_psci_ops_t rcar_plat_psci_ops = { 198*f180a3b7SHieu Nguyen .cpu_standby = rcar_cpu_standby, 199*f180a3b7SHieu Nguyen .pwr_domain_on = rcar_pwr_domain_on, 200*f180a3b7SHieu Nguyen .pwr_domain_off = rcar_pwr_domain_off, 201*f180a3b7SHieu Nguyen .pwr_domain_suspend = rcar_pwr_domain_suspend, 202*f180a3b7SHieu Nguyen .pwr_domain_on_finish = rcar_pwr_domain_on_finish, 203*f180a3b7SHieu Nguyen .pwr_domain_suspend_finish = rcar_pwr_domain_suspend_finish, 204*f180a3b7SHieu Nguyen .system_off = rcar_system_off, 205*f180a3b7SHieu Nguyen .system_reset = rcar_system_reset, 206*f180a3b7SHieu Nguyen .validate_power_state = rcar_validate_power_state, 207*f180a3b7SHieu Nguyen .pwr_domain_pwr_down = rcar_pwr_domain_pwr_down_wfi, 208*f180a3b7SHieu Nguyen .get_sys_suspend_power_state = rcar_get_sys_suspend_power_state, 209*f180a3b7SHieu Nguyen }; 210*f180a3b7SHieu Nguyen 211*f180a3b7SHieu Nguyen int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) 212*f180a3b7SHieu Nguyen { 213*f180a3b7SHieu Nguyen *psci_ops = plat_rcar_psci_override_pm_ops(&rcar_plat_psci_ops); 214*f180a3b7SHieu Nguyen rcar_sec_entrypoint = sec_entrypoint; 215*f180a3b7SHieu Nguyen 216*f180a3b7SHieu Nguyen return 0; 217*f180a3b7SHieu Nguyen } 218