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 #include "timer.h" 20 21 #include "platform_def.h" 22 #include "rcar_def.h" 23 #include "rcar_private.h" 24 25 #define SYSTEM_PWR_STATE(s) ((s)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 26 #define CLUSTER_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL1]) 27 #define CORE_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL0]) 28 29 static uintptr_t rcar_sec_entrypoint; 30 static gicv3_redist_ctx_t rdist_ctx[PLATFORM_CORE_COUNT]; 31 static gicv3_dist_ctx_t dist_ctx; 32 33 static void rcar_program_mailbox(u_register_t mpidr, uintptr_t address) 34 { 35 const int linear_id = plat_core_pos_by_mpidr(mpidr); 36 void *mbox_addr = (void *)MBOX_BASE + (CACHE_WRITEBACK_GRANULE * linear_id); 37 uint64_t *value = (uint64_t *)mbox_addr; 38 39 if (linear_id < 0) { 40 ERROR("BL3-1 : The value of passed MPIDR is invalid."); 41 panic(); 42 } 43 44 *value = address; 45 46 flush_dcache_range((uintptr_t)value, CACHE_WRITEBACK_GRANULE); 47 } 48 49 static void rcar_cpu_standby(plat_local_state_t cpu_state) 50 { 51 u_register_t scr_el3 = read_scr_el3(); 52 53 write_scr_el3(scr_el3 | SCR_IRQ_BIT); 54 dsb(); 55 wfi(); 56 write_scr_el3(scr_el3); 57 } 58 59 static int rcar_pwr_domain_on(u_register_t mpidr) 60 { 61 rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 62 rcar_pwrc_cpuon(mpidr); 63 64 return PSCI_E_SUCCESS; 65 } 66 67 static void rcar_pwr_domain_on_finish(const psci_power_state_t *target_state) 68 { 69 u_register_t mpidr = read_mpidr_el1(); 70 71 rcar_pwrc_disable_interrupt_wakeup(mpidr); 72 rcar_program_mailbox(mpidr, 0U); 73 } 74 75 static void rcar_pwr_domain_off(const psci_power_state_t *target_state) 76 { 77 u_register_t mpidr = read_mpidr_el1(); 78 79 rcar_pwrc_disable_interrupt_wakeup(mpidr); 80 81 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 82 rcar_pwrc_clusteroff(mpidr); 83 } else { 84 rcar_pwrc_cpuoff(mpidr); 85 } 86 } 87 88 static void rcar_pwr_domain_suspend(const psci_power_state_t *target_state) 89 { 90 u_register_t mpidr = read_mpidr_el1(); 91 92 if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) { 93 return; 94 } 95 96 rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 97 rcar_pwrc_enable_interrupt_wakeup(mpidr); 98 99 if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 100 for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) 101 gicv3_rdistif_save(i, &rdist_ctx[i]); 102 gicv3_distif_save(&dist_ctx); 103 } 104 105 if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 106 rcar_pwrc_clusteroff(mpidr); 107 } else { 108 rcar_pwrc_cpuoff(mpidr); 109 } 110 } 111 112 static void rcar_pwr_domain_suspend_finish(const psci_power_state_t 113 *target_state) 114 { 115 u_register_t mpidr = read_mpidr_el1(); 116 117 if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 118 rcar_pwrc_restore_timer_state(); 119 rcar_pwrc_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 147 static void rcar_system_reset(void) 148 { 149 mmio_write_32(RCAR_SRESCR, 0x5AA50000U | BIT(15)); 150 } 151 152 static void rcar_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 153 { 154 if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 155 rcar_pwrc_suspend_to_ram(); 156 } 157 158 static int rcar_validate_power_state(unsigned int power_state, 159 psci_power_state_t *req_state) 160 { 161 uint32_t pwr_lvl = psci_get_pstate_pwrlvl(power_state); 162 uint32_t pstate = psci_get_pstate_type(power_state); 163 uint64_t i; 164 165 if (pstate == PSTATE_TYPE_STANDBY) { 166 if (pwr_lvl != MPIDR_AFFLVL0) 167 return PSCI_E_INVALID_PARAMS; 168 169 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 170 } else { 171 for (i = MPIDR_AFFLVL0; i <= (uint64_t)pwr_lvl; i++) 172 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 173 } 174 175 if (psci_get_pstate_id(power_state) != 0U) 176 return PSCI_E_INVALID_PARAMS; 177 178 return PSCI_E_SUCCESS; 179 } 180 181 static void rcar_get_sys_suspend_power_state(psci_power_state_t *req_state) 182 { 183 uint64_t i; 184 u_register_t mpidr = read_mpidr_el1(); 185 186 if (!rcar_pwrc_mpidr_is_boot_cpu(mpidr)) { 187 /* deny system suspend entry */ 188 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = 189 PSCI_LOCAL_STATE_RUN; 190 191 for (i = MPIDR_AFFLVL0; i < (uint64_t)PLAT_MAX_PWR_LVL; i++) 192 req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; 193 } else { 194 for (i = MPIDR_AFFLVL0; i <= (uint64_t)PLAT_MAX_PWR_LVL; i++) 195 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 196 } 197 } 198 199 static plat_psci_ops_t rcar_plat_psci_ops = { 200 .cpu_standby = rcar_cpu_standby, 201 .pwr_domain_on = rcar_pwr_domain_on, 202 .pwr_domain_off = rcar_pwr_domain_off, 203 .pwr_domain_suspend = rcar_pwr_domain_suspend, 204 .pwr_domain_on_finish = rcar_pwr_domain_on_finish, 205 .pwr_domain_suspend_finish = rcar_pwr_domain_suspend_finish, 206 .system_off = rcar_system_off, 207 .system_reset = rcar_system_reset, 208 .validate_power_state = rcar_validate_power_state, 209 .pwr_domain_pwr_down = rcar_pwr_domain_pwr_down_wfi, 210 .get_sys_suspend_power_state = rcar_get_sys_suspend_power_state, 211 }; 212 213 int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) 214 { 215 *psci_ops = &rcar_plat_psci_ops; 216 rcar_sec_entrypoint = sec_entrypoint; 217 218 return 0; 219 } 220