1499c2713SBiju Das /* 2*92196d4fSMarek Vasut * Copyright (c) 2015-2025, Renesas Electronics Corporation. All rights reserved. 3499c2713SBiju Das * 4499c2713SBiju Das * SPDX-License-Identifier: BSD-3-Clause 5499c2713SBiju Das */ 6499c2713SBiju Das 7499c2713SBiju Das #include <errno.h> 8499c2713SBiju Das 9499c2713SBiju Das #include <arch_helpers.h> 10499c2713SBiju Das #include <common/bl_common.h> 11499c2713SBiju Das #include <common/debug.h> 12499c2713SBiju Das #include <drivers/arm/cci.h> 13499c2713SBiju Das #include <drivers/arm/gicv2.h> 14499c2713SBiju Das #include <lib/bakery_lock.h> 15499c2713SBiju Das #include <lib/mmio.h> 16499c2713SBiju Das #include <lib/psci/psci.h> 17499c2713SBiju Das #include <plat/common/platform.h> 18499c2713SBiju Das 19499c2713SBiju Das #include "iic_dvfs.h" 20499c2713SBiju Das #include "platform_def.h" 21499c2713SBiju Das #include "pwrc.h" 22*92196d4fSMarek Vasut #include "timer.h" 23*92196d4fSMarek Vasut 24499c2713SBiju Das #include "rcar_def.h" 25499c2713SBiju Das #include "rcar_private.h" 26*92196d4fSMarek Vasut 2727bbfca9SBiju Das #if RCAR_GEN3_ULCB 28499c2713SBiju Das #include "ulcb_cpld.h" 2927bbfca9SBiju Das #endif /* RCAR_GEN3_ULCB */ 30499c2713SBiju Das 31499c2713SBiju Das #define DVFS_SET_VID_0V (0x00) 32499c2713SBiju Das #define P_ALL_OFF (0x80) 33499c2713SBiju Das #define KEEPON_DDR1C (0x08) 34499c2713SBiju Das #define KEEPON_DDR0C (0x04) 35499c2713SBiju Das #define KEEPON_DDR1 (0x02) 36499c2713SBiju Das #define KEEPON_DDR0 (0x01) 37499c2713SBiju Das 38499c2713SBiju Das #define SYSTEM_PWR_STATE(s) ((s)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 39499c2713SBiju Das #define CLUSTER_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL1]) 40499c2713SBiju Das #define CORE_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL0]) 41499c2713SBiju Das 42499c2713SBiju Das extern void rcar_pwrc_restore_generic_timer(uint64_t *stack); 43499c2713SBiju Das extern void plat_rcar_gic_driver_init(void); 44499c2713SBiju Das extern void plat_rcar_gic_init(void); 45499c2713SBiju Das 46499c2713SBiju Das static uintptr_t rcar_sec_entrypoint; 47499c2713SBiju Das 48ffb725beSTakuya Sakata static void rcar_program_mailbox(u_register_t mpidr, uintptr_t address) 49499c2713SBiju Das { 50499c2713SBiju Das mailbox_t *rcar_mboxes = (mailbox_t *) MBOX_BASE; 51499c2713SBiju Das uint64_t linear_id = plat_core_pos_by_mpidr(mpidr); 52499c2713SBiju Das unsigned long range; 53499c2713SBiju Das 54499c2713SBiju Das rcar_mboxes[linear_id].value = address; 55499c2713SBiju Das range = (unsigned long)&rcar_mboxes[linear_id]; 56499c2713SBiju Das 57499c2713SBiju Das flush_dcache_range(range, sizeof(range)); 58499c2713SBiju Das } 59499c2713SBiju Das 60499c2713SBiju Das static void rcar_cpu_standby(plat_local_state_t cpu_state) 61499c2713SBiju Das { 62499c2713SBiju Das u_register_t scr_el3 = read_scr_el3(); 63499c2713SBiju Das 64499c2713SBiju Das write_scr_el3(scr_el3 | SCR_IRQ_BIT); 65499c2713SBiju Das dsb(); 66499c2713SBiju Das wfi(); 67499c2713SBiju Das write_scr_el3(scr_el3); 68499c2713SBiju Das } 69499c2713SBiju Das 70499c2713SBiju Das static int rcar_pwr_domain_on(u_register_t mpidr) 71499c2713SBiju Das { 72499c2713SBiju Das rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 73499c2713SBiju Das rcar_pwrc_cpuon(mpidr); 74499c2713SBiju Das 75499c2713SBiju Das return PSCI_E_SUCCESS; 76499c2713SBiju Das } 77499c2713SBiju Das 78499c2713SBiju Das static void rcar_pwr_domain_on_finish(const psci_power_state_t *target_state) 79499c2713SBiju Das { 80499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 81ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 82499c2713SBiju Das 83499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 84499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 85499c2713SBiju Das plat_cci_enable(); 86499c2713SBiju Das 87499c2713SBiju Das rcar_program_mailbox(mpidr, 0); 88d9912cf3STakuya Sakata rcar_pwrc_enable_interrupt_wakeup(mpidr); 89499c2713SBiju Das 90499c2713SBiju Das gicv2_cpuif_enable(); 91499c2713SBiju Das gicv2_pcpu_distif_init(); 92499c2713SBiju Das } 93499c2713SBiju Das 94499c2713SBiju Das static void rcar_pwr_domain_off(const psci_power_state_t *target_state) 95499c2713SBiju Das { 96499c2713SBiju Das #if RCAR_LSI != RCAR_D3 97499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 98499c2713SBiju Das #endif 99ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 100499c2713SBiju Das 101d9912cf3STakuya Sakata rcar_pwrc_disable_interrupt_wakeup(mpidr); 102499c2713SBiju Das gicv2_cpuif_disable(); 103499c2713SBiju Das rcar_pwrc_cpuoff(mpidr); 104499c2713SBiju Das 105499c2713SBiju Das #if RCAR_LSI != RCAR_D3 106499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 107499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 108499c2713SBiju Das plat_cci_disable(); 109499c2713SBiju Das 110499c2713SBiju Das rcar_pwrc_clusteroff(mpidr); 111499c2713SBiju Das } 112499c2713SBiju Das #endif 113499c2713SBiju Das } 114499c2713SBiju Das 115499c2713SBiju Das static void rcar_pwr_domain_suspend(const psci_power_state_t *target_state) 116499c2713SBiju Das { 117499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 118ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 119499c2713SBiju Das 120499c2713SBiju Das if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 121499c2713SBiju Das return; 122499c2713SBiju Das 123499c2713SBiju Das rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 124499c2713SBiju Das rcar_pwrc_enable_interrupt_wakeup(mpidr); 125499c2713SBiju Das gicv2_cpuif_disable(); 126499c2713SBiju Das rcar_pwrc_cpuoff(mpidr); 127499c2713SBiju Das 128499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 129499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 130499c2713SBiju Das plat_cci_disable(); 131499c2713SBiju Das 132499c2713SBiju Das rcar_pwrc_clusteroff(mpidr); 133499c2713SBiju Das } 134499c2713SBiju Das } 135499c2713SBiju Das 136499c2713SBiju Das static void rcar_pwr_domain_suspend_finish(const psci_power_state_t 137499c2713SBiju Das *target_state) 138499c2713SBiju Das { 139499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 140499c2713SBiju Das 141499c2713SBiju Das if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 142499c2713SBiju Das goto finish; 143499c2713SBiju Das 144499c2713SBiju Das plat_rcar_gic_driver_init(); 145499c2713SBiju Das plat_rcar_gic_init(); 146499c2713SBiju Das 147499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 148499c2713SBiju Das plat_cci_init(); 149499c2713SBiju Das 150499c2713SBiju Das rcar_pwrc_restore_timer_state(); 151499c2713SBiju Das rcar_pwrc_setup(); 152499c2713SBiju Das rcar_pwrc_code_copy_to_system_ram(); 153499c2713SBiju Das 154499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 155499c2713SBiju Das rcar_pwrc_init_suspend_to_ram(); 156499c2713SBiju Das #endif 157499c2713SBiju Das finish: 158499c2713SBiju Das rcar_pwr_domain_on_finish(target_state); 159499c2713SBiju Das } 160499c2713SBiju Das 161731aa26fSToshiyuki Ogasahara static void __dead2 rcar_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 162731aa26fSToshiyuki Ogasahara { 163731aa26fSToshiyuki Ogasahara #if RCAR_SYSTEM_SUSPEND 164731aa26fSToshiyuki Ogasahara if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 165731aa26fSToshiyuki Ogasahara rcar_pwrc_suspend_to_ram(); 166731aa26fSToshiyuki Ogasahara #endif 167731aa26fSToshiyuki Ogasahara wfi(); 168731aa26fSToshiyuki Ogasahara 169731aa26fSToshiyuki Ogasahara ERROR("RCAR Power Down: operation not handled.\n"); 170731aa26fSToshiyuki Ogasahara panic(); 171731aa26fSToshiyuki Ogasahara } 172731aa26fSToshiyuki Ogasahara 173499c2713SBiju Das static void __dead2 rcar_system_off(void) 174499c2713SBiju Das { 175499c2713SBiju Das #if PMIC_ROHM_BD9571 176499c2713SBiju Das #if PMIC_LEVEL_MODE 177499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, DVFS_SET_VID, DVFS_SET_VID_0V)) 178499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-OFF.\n"); 179499c2713SBiju Das #else 180499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) 181499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); 182499c2713SBiju Das #endif 183499c2713SBiju Das #else 1841b49ba0fSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 1851b49ba0fSTakuya Sakata u_register_t cpu = mpidr & 0x0000ffffU; 186499c2713SBiju Das int32_t rtn_on; 187499c2713SBiju Das 1881b49ba0fSTakuya Sakata rtn_on = rcar_pwrc_cpu_on_check(mpidr); 189499c2713SBiju Das 1901b49ba0fSTakuya Sakata if (cpu != rcar_boot_mpidr) { 191499c2713SBiju Das panic(); 1921b49ba0fSTakuya Sakata } 193499c2713SBiju Das 1941b49ba0fSTakuya Sakata if (rtn_on != 0) { 195499c2713SBiju Das panic(); 1961b49ba0fSTakuya Sakata } 197499c2713SBiju Das 1981b49ba0fSTakuya Sakata rcar_pwrc_cpuoff(mpidr); 1991b49ba0fSTakuya Sakata rcar_pwrc_clusteroff(mpidr); 200499c2713SBiju Das 201499c2713SBiju Das #endif /* PMIC_ROHM_BD9571 */ 202499c2713SBiju Das wfi(); 203499c2713SBiju Das ERROR("RCAR System Off: operation not handled.\n"); 204499c2713SBiju Das panic(); 205499c2713SBiju Das } 206499c2713SBiju Das 207499c2713SBiju Das static void __dead2 rcar_system_reset(void) 208499c2713SBiju Das { 209499c2713SBiju Das #if PMIC_ROHM_BD9571 210499c2713SBiju Das #if PMIC_LEVEL_MODE 211499c2713SBiju Das #if RCAR_SYSTEM_RESET_KEEPON_DDR 212499c2713SBiju Das uint8_t mode; 213499c2713SBiju Das int32_t error; 214499c2713SBiju Das 215499c2713SBiju Das error = rcar_iic_dvfs_send(PMIC, REG_KEEP10, KEEP10_MAGIC); 216499c2713SBiju Das if (error) { 217499c2713SBiju Das ERROR("Failed send KEEP10 magic ret=%d\n", error); 218499c2713SBiju Das goto done; 219499c2713SBiju Das } 220499c2713SBiju Das 221499c2713SBiju Das error = rcar_iic_dvfs_receive(PMIC, BKUP_MODE_CNT, &mode); 222499c2713SBiju Das if (error) { 223499c2713SBiju Das ERROR("Failed receive BKUP_Mode_Cnt ret=%d\n", error); 224499c2713SBiju Das goto done; 225499c2713SBiju Das } 226499c2713SBiju Das 227499c2713SBiju Das mode |= KEEPON_DDR1C | KEEPON_DDR0C | KEEPON_DDR1 | KEEPON_DDR0; 228499c2713SBiju Das error = rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, mode); 229499c2713SBiju Das if (error) { 230499c2713SBiju Das ERROR("Failed send KEEPON_DDRx ret=%d\n", error); 231499c2713SBiju Das goto done; 232499c2713SBiju Das } 233499c2713SBiju Das 234499c2713SBiju Das rcar_pwrc_set_suspend_to_ram(); 235499c2713SBiju Das done: 236499c2713SBiju Das #else 237499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) 238499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); 239499c2713SBiju Das #endif 240499c2713SBiju Das #else 241499c2713SBiju Das #if (RCAR_GEN3_ULCB == 1) 242499c2713SBiju Das rcar_cpld_reset_cpu(); 243499c2713SBiju Das #endif 244499c2713SBiju Das #endif 245499c2713SBiju Das #else 246499c2713SBiju Das rcar_pwrc_system_reset(); 247499c2713SBiju Das #endif 248499c2713SBiju Das wfi(); 249499c2713SBiju Das 250499c2713SBiju Das ERROR("RCAR System Reset: operation not handled.\n"); 251499c2713SBiju Das panic(); 252499c2713SBiju Das } 253499c2713SBiju Das 254499c2713SBiju Das static int rcar_validate_power_state(unsigned int power_state, 255499c2713SBiju Das psci_power_state_t *req_state) 256499c2713SBiju Das { 257499c2713SBiju Das unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 258499c2713SBiju Das unsigned int pstate = psci_get_pstate_type(power_state); 259499c2713SBiju Das uint32_t i; 260499c2713SBiju Das 261499c2713SBiju Das if (pstate == PSTATE_TYPE_STANDBY) { 262499c2713SBiju Das if (pwr_lvl != MPIDR_AFFLVL0) 263499c2713SBiju Das return PSCI_E_INVALID_PARAMS; 264499c2713SBiju Das 265499c2713SBiju Das req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 266499c2713SBiju Das } else { 267499c2713SBiju Das for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 268499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 269499c2713SBiju Das } 270499c2713SBiju Das 271499c2713SBiju Das if (psci_get_pstate_id(power_state)) 272499c2713SBiju Das return PSCI_E_INVALID_PARAMS; 273499c2713SBiju Das 274499c2713SBiju Das return PSCI_E_SUCCESS; 275499c2713SBiju Das } 276499c2713SBiju Das 277499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 278499c2713SBiju Das static void rcar_get_sys_suspend_power_state(psci_power_state_t *req_state) 279499c2713SBiju Das { 280ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1() & 0x0000ffffU; 281499c2713SBiju Das int i; 282499c2713SBiju Das 283499c2713SBiju Das if (mpidr != rcar_boot_mpidr) 284499c2713SBiju Das goto deny; 285499c2713SBiju Das 286499c2713SBiju Das for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 287499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 288499c2713SBiju Das 289499c2713SBiju Das return; 290499c2713SBiju Das deny: 291499c2713SBiju Das /* deny system suspend entry */ 292499c2713SBiju Das req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PSCI_LOCAL_STATE_RUN; 293499c2713SBiju Das for (i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++) 294499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; 295499c2713SBiju Das } 296499c2713SBiju Das #endif 297499c2713SBiju Das 298499c2713SBiju Das static const plat_psci_ops_t rcar_plat_psci_ops = { 299499c2713SBiju Das .cpu_standby = rcar_cpu_standby, 300499c2713SBiju Das .pwr_domain_on = rcar_pwr_domain_on, 301499c2713SBiju Das .pwr_domain_off = rcar_pwr_domain_off, 302499c2713SBiju Das .pwr_domain_suspend = rcar_pwr_domain_suspend, 303499c2713SBiju Das .pwr_domain_on_finish = rcar_pwr_domain_on_finish, 304499c2713SBiju Das .pwr_domain_suspend_finish = rcar_pwr_domain_suspend_finish, 305499c2713SBiju Das .system_off = rcar_system_off, 306499c2713SBiju Das .system_reset = rcar_system_reset, 307499c2713SBiju Das .validate_power_state = rcar_validate_power_state, 308db5fe4f4SBoyan Karatotev .pwr_domain_pwr_down = rcar_pwr_domain_pwr_down_wfi, 309499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 310499c2713SBiju Das .get_sys_suspend_power_state = rcar_get_sys_suspend_power_state, 311499c2713SBiju Das #endif 312499c2713SBiju Das }; 313499c2713SBiju Das 314499c2713SBiju Das int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) 315499c2713SBiju Das { 316499c2713SBiju Das *psci_ops = &rcar_plat_psci_ops; 317499c2713SBiju Das rcar_sec_entrypoint = sec_entrypoint; 318499c2713SBiju Das 319499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 320499c2713SBiju Das rcar_pwrc_init_suspend_to_ram(); 321499c2713SBiju Das #endif 322499c2713SBiju Das return 0; 323499c2713SBiju Das } 324499c2713SBiju Das 325