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