1499c2713SBiju Das /* 2731aa26fSToshiyuki Ogasahara * Copyright (c) 2015-2021, 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" 22499c2713SBiju Das #include "rcar_def.h" 23499c2713SBiju Das #include "rcar_private.h" 2427bbfca9SBiju Das #if RCAR_GEN3_ULCB 25499c2713SBiju Das #include "ulcb_cpld.h" 2627bbfca9SBiju Das #endif /* RCAR_GEN3_ULCB */ 27499c2713SBiju Das 28499c2713SBiju Das #define DVFS_SET_VID_0V (0x00) 29499c2713SBiju Das #define P_ALL_OFF (0x80) 30499c2713SBiju Das #define KEEPON_DDR1C (0x08) 31499c2713SBiju Das #define KEEPON_DDR0C (0x04) 32499c2713SBiju Das #define KEEPON_DDR1 (0x02) 33499c2713SBiju Das #define KEEPON_DDR0 (0x01) 34499c2713SBiju Das 35499c2713SBiju Das #define SYSTEM_PWR_STATE(s) ((s)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 36499c2713SBiju Das #define CLUSTER_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL1]) 37499c2713SBiju Das #define CORE_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL0]) 38499c2713SBiju Das 39499c2713SBiju Das extern void rcar_pwrc_restore_generic_timer(uint64_t *stack); 40499c2713SBiju Das extern void plat_rcar_gic_driver_init(void); 41499c2713SBiju Das extern void plat_rcar_gic_init(void); 42499c2713SBiju Das 43499c2713SBiju Das static uintptr_t rcar_sec_entrypoint; 44499c2713SBiju Das 45ffb725beSTakuya Sakata static void rcar_program_mailbox(u_register_t mpidr, uintptr_t address) 46499c2713SBiju Das { 47499c2713SBiju Das mailbox_t *rcar_mboxes = (mailbox_t *) MBOX_BASE; 48499c2713SBiju Das uint64_t linear_id = plat_core_pos_by_mpidr(mpidr); 49499c2713SBiju Das unsigned long range; 50499c2713SBiju Das 51499c2713SBiju Das rcar_mboxes[linear_id].value = address; 52499c2713SBiju Das range = (unsigned long)&rcar_mboxes[linear_id]; 53499c2713SBiju Das 54499c2713SBiju Das flush_dcache_range(range, sizeof(range)); 55499c2713SBiju Das } 56499c2713SBiju Das 57499c2713SBiju Das static void rcar_cpu_standby(plat_local_state_t cpu_state) 58499c2713SBiju Das { 59499c2713SBiju Das u_register_t scr_el3 = read_scr_el3(); 60499c2713SBiju Das 61499c2713SBiju Das write_scr_el3(scr_el3 | SCR_IRQ_BIT); 62499c2713SBiju Das dsb(); 63499c2713SBiju Das wfi(); 64499c2713SBiju Das write_scr_el3(scr_el3); 65499c2713SBiju Das } 66499c2713SBiju Das 67499c2713SBiju Das static int rcar_pwr_domain_on(u_register_t mpidr) 68499c2713SBiju Das { 69499c2713SBiju Das rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 70499c2713SBiju Das rcar_pwrc_cpuon(mpidr); 71499c2713SBiju Das 72499c2713SBiju Das return PSCI_E_SUCCESS; 73499c2713SBiju Das } 74499c2713SBiju Das 75499c2713SBiju Das static void rcar_pwr_domain_on_finish(const psci_power_state_t *target_state) 76499c2713SBiju Das { 77499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 78ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 79499c2713SBiju Das 80499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 81499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 82499c2713SBiju Das plat_cci_enable(); 83499c2713SBiju Das 84499c2713SBiju Das rcar_program_mailbox(mpidr, 0); 85d9912cf3STakuya Sakata rcar_pwrc_enable_interrupt_wakeup(mpidr); 86499c2713SBiju Das 87499c2713SBiju Das gicv2_cpuif_enable(); 88499c2713SBiju Das gicv2_pcpu_distif_init(); 89499c2713SBiju Das } 90499c2713SBiju Das 91499c2713SBiju Das static void rcar_pwr_domain_off(const psci_power_state_t *target_state) 92499c2713SBiju Das { 93499c2713SBiju Das #if RCAR_LSI != RCAR_D3 94499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 95499c2713SBiju Das #endif 96ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 97499c2713SBiju Das 98d9912cf3STakuya Sakata rcar_pwrc_disable_interrupt_wakeup(mpidr); 99499c2713SBiju Das gicv2_cpuif_disable(); 100499c2713SBiju Das rcar_pwrc_cpuoff(mpidr); 101499c2713SBiju Das 102499c2713SBiju Das #if RCAR_LSI != RCAR_D3 103499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 104499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 105499c2713SBiju Das plat_cci_disable(); 106499c2713SBiju Das 107499c2713SBiju Das rcar_pwrc_clusteroff(mpidr); 108499c2713SBiju Das } 109499c2713SBiju Das #endif 110499c2713SBiju Das } 111499c2713SBiju Das 112499c2713SBiju Das static void rcar_pwr_domain_suspend(const psci_power_state_t *target_state) 113499c2713SBiju Das { 114499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 115ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 116499c2713SBiju Das 117499c2713SBiju Das if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 118499c2713SBiju Das return; 119499c2713SBiju Das 120499c2713SBiju Das rcar_program_mailbox(mpidr, rcar_sec_entrypoint); 121499c2713SBiju Das rcar_pwrc_enable_interrupt_wakeup(mpidr); 122499c2713SBiju Das gicv2_cpuif_disable(); 123499c2713SBiju Das rcar_pwrc_cpuoff(mpidr); 124499c2713SBiju Das 125499c2713SBiju Das if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 126499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 127499c2713SBiju Das plat_cci_disable(); 128499c2713SBiju Das 129499c2713SBiju Das rcar_pwrc_clusteroff(mpidr); 130499c2713SBiju Das } 131499c2713SBiju Das } 132499c2713SBiju Das 133499c2713SBiju Das static void rcar_pwr_domain_suspend_finish(const psci_power_state_t 134499c2713SBiju Das *target_state) 135499c2713SBiju Das { 136499c2713SBiju Das uint32_t cluster_type = rcar_pwrc_get_cluster(); 137499c2713SBiju Das 138499c2713SBiju Das if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 139499c2713SBiju Das goto finish; 140499c2713SBiju Das 141499c2713SBiju Das plat_rcar_gic_driver_init(); 142499c2713SBiju Das plat_rcar_gic_init(); 143499c2713SBiju Das 144499c2713SBiju Das if (cluster_type == RCAR_CLUSTER_A53A57) 145499c2713SBiju Das plat_cci_init(); 146499c2713SBiju Das 147499c2713SBiju Das rcar_pwrc_restore_timer_state(); 148499c2713SBiju Das rcar_pwrc_setup(); 149499c2713SBiju Das rcar_pwrc_code_copy_to_system_ram(); 150499c2713SBiju Das 151499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 152499c2713SBiju Das rcar_pwrc_init_suspend_to_ram(); 153499c2713SBiju Das #endif 154499c2713SBiju Das finish: 155499c2713SBiju Das rcar_pwr_domain_on_finish(target_state); 156499c2713SBiju Das } 157499c2713SBiju Das 158731aa26fSToshiyuki Ogasahara static void __dead2 rcar_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 159731aa26fSToshiyuki Ogasahara { 160731aa26fSToshiyuki Ogasahara #if RCAR_SYSTEM_SUSPEND 161731aa26fSToshiyuki Ogasahara if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 162731aa26fSToshiyuki Ogasahara rcar_pwrc_suspend_to_ram(); 163731aa26fSToshiyuki Ogasahara #endif 164731aa26fSToshiyuki Ogasahara wfi(); 165731aa26fSToshiyuki Ogasahara 166731aa26fSToshiyuki Ogasahara ERROR("RCAR Power Down: operation not handled.\n"); 167731aa26fSToshiyuki Ogasahara panic(); 168731aa26fSToshiyuki Ogasahara } 169731aa26fSToshiyuki Ogasahara 170499c2713SBiju Das static void __dead2 rcar_system_off(void) 171499c2713SBiju Das { 172499c2713SBiju Das #if PMIC_ROHM_BD9571 173499c2713SBiju Das #if PMIC_LEVEL_MODE 174499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, DVFS_SET_VID, DVFS_SET_VID_0V)) 175499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-OFF.\n"); 176499c2713SBiju Das #else 177499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) 178499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); 179499c2713SBiju Das #endif 180499c2713SBiju Das #else 181*1b49ba0fSTakuya Sakata u_register_t mpidr = read_mpidr_el1(); 182*1b49ba0fSTakuya Sakata u_register_t cpu = mpidr & 0x0000ffffU; 183499c2713SBiju Das int32_t rtn_on; 184499c2713SBiju Das 185*1b49ba0fSTakuya Sakata rtn_on = rcar_pwrc_cpu_on_check(mpidr); 186499c2713SBiju Das 187*1b49ba0fSTakuya Sakata if (cpu != rcar_boot_mpidr) { 188499c2713SBiju Das panic(); 189*1b49ba0fSTakuya Sakata } 190499c2713SBiju Das 191*1b49ba0fSTakuya Sakata if (rtn_on != 0) { 192499c2713SBiju Das panic(); 193*1b49ba0fSTakuya Sakata } 194499c2713SBiju Das 195*1b49ba0fSTakuya Sakata rcar_pwrc_cpuoff(mpidr); 196*1b49ba0fSTakuya Sakata rcar_pwrc_clusteroff(mpidr); 197499c2713SBiju Das 198499c2713SBiju Das #endif /* PMIC_ROHM_BD9571 */ 199499c2713SBiju Das wfi(); 200499c2713SBiju Das ERROR("RCAR System Off: operation not handled.\n"); 201499c2713SBiju Das panic(); 202499c2713SBiju Das } 203499c2713SBiju Das 204499c2713SBiju Das static void __dead2 rcar_system_reset(void) 205499c2713SBiju Das { 206499c2713SBiju Das #if PMIC_ROHM_BD9571 207499c2713SBiju Das #if PMIC_LEVEL_MODE 208499c2713SBiju Das #if RCAR_SYSTEM_RESET_KEEPON_DDR 209499c2713SBiju Das uint8_t mode; 210499c2713SBiju Das int32_t error; 211499c2713SBiju Das 212499c2713SBiju Das error = rcar_iic_dvfs_send(PMIC, REG_KEEP10, KEEP10_MAGIC); 213499c2713SBiju Das if (error) { 214499c2713SBiju Das ERROR("Failed send KEEP10 magic ret=%d\n", error); 215499c2713SBiju Das goto done; 216499c2713SBiju Das } 217499c2713SBiju Das 218499c2713SBiju Das error = rcar_iic_dvfs_receive(PMIC, BKUP_MODE_CNT, &mode); 219499c2713SBiju Das if (error) { 220499c2713SBiju Das ERROR("Failed receive BKUP_Mode_Cnt ret=%d\n", error); 221499c2713SBiju Das goto done; 222499c2713SBiju Das } 223499c2713SBiju Das 224499c2713SBiju Das mode |= KEEPON_DDR1C | KEEPON_DDR0C | KEEPON_DDR1 | KEEPON_DDR0; 225499c2713SBiju Das error = rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, mode); 226499c2713SBiju Das if (error) { 227499c2713SBiju Das ERROR("Failed send KEEPON_DDRx ret=%d\n", error); 228499c2713SBiju Das goto done; 229499c2713SBiju Das } 230499c2713SBiju Das 231499c2713SBiju Das rcar_pwrc_set_suspend_to_ram(); 232499c2713SBiju Das done: 233499c2713SBiju Das #else 234499c2713SBiju Das if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) 235499c2713SBiju Das ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); 236499c2713SBiju Das #endif 237499c2713SBiju Das #else 238499c2713SBiju Das #if (RCAR_GEN3_ULCB == 1) 239499c2713SBiju Das rcar_cpld_reset_cpu(); 240499c2713SBiju Das #endif 241499c2713SBiju Das #endif 242499c2713SBiju Das #else 243499c2713SBiju Das rcar_pwrc_system_reset(); 244499c2713SBiju Das #endif 245499c2713SBiju Das wfi(); 246499c2713SBiju Das 247499c2713SBiju Das ERROR("RCAR System Reset: operation not handled.\n"); 248499c2713SBiju Das panic(); 249499c2713SBiju Das } 250499c2713SBiju Das 251499c2713SBiju Das static int rcar_validate_power_state(unsigned int power_state, 252499c2713SBiju Das psci_power_state_t *req_state) 253499c2713SBiju Das { 254499c2713SBiju Das unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 255499c2713SBiju Das unsigned int pstate = psci_get_pstate_type(power_state); 256499c2713SBiju Das uint32_t i; 257499c2713SBiju Das 258499c2713SBiju Das if (pstate == PSTATE_TYPE_STANDBY) { 259499c2713SBiju Das if (pwr_lvl != MPIDR_AFFLVL0) 260499c2713SBiju Das return PSCI_E_INVALID_PARAMS; 261499c2713SBiju Das 262499c2713SBiju Das req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 263499c2713SBiju Das } else { 264499c2713SBiju Das for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 265499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 266499c2713SBiju Das } 267499c2713SBiju Das 268499c2713SBiju Das if (psci_get_pstate_id(power_state)) 269499c2713SBiju Das return PSCI_E_INVALID_PARAMS; 270499c2713SBiju Das 271499c2713SBiju Das return PSCI_E_SUCCESS; 272499c2713SBiju Das } 273499c2713SBiju Das 274499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 275499c2713SBiju Das static void rcar_get_sys_suspend_power_state(psci_power_state_t *req_state) 276499c2713SBiju Das { 277ffb725beSTakuya Sakata u_register_t mpidr = read_mpidr_el1() & 0x0000ffffU; 278499c2713SBiju Das int i; 279499c2713SBiju Das 280499c2713SBiju Das if (mpidr != rcar_boot_mpidr) 281499c2713SBiju Das goto deny; 282499c2713SBiju Das 283499c2713SBiju Das for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 284499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 285499c2713SBiju Das 286499c2713SBiju Das return; 287499c2713SBiju Das deny: 288499c2713SBiju Das /* deny system suspend entry */ 289499c2713SBiju Das req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PSCI_LOCAL_STATE_RUN; 290499c2713SBiju Das for (i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++) 291499c2713SBiju Das req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; 292499c2713SBiju Das } 293499c2713SBiju Das #endif 294499c2713SBiju Das 295499c2713SBiju Das static const plat_psci_ops_t rcar_plat_psci_ops = { 296499c2713SBiju Das .cpu_standby = rcar_cpu_standby, 297499c2713SBiju Das .pwr_domain_on = rcar_pwr_domain_on, 298499c2713SBiju Das .pwr_domain_off = rcar_pwr_domain_off, 299499c2713SBiju Das .pwr_domain_suspend = rcar_pwr_domain_suspend, 300499c2713SBiju Das .pwr_domain_on_finish = rcar_pwr_domain_on_finish, 301499c2713SBiju Das .pwr_domain_suspend_finish = rcar_pwr_domain_suspend_finish, 302499c2713SBiju Das .system_off = rcar_system_off, 303499c2713SBiju Das .system_reset = rcar_system_reset, 304499c2713SBiju Das .validate_power_state = rcar_validate_power_state, 305731aa26fSToshiyuki Ogasahara .pwr_domain_pwr_down_wfi = rcar_pwr_domain_pwr_down_wfi, 306499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 307499c2713SBiju Das .get_sys_suspend_power_state = rcar_get_sys_suspend_power_state, 308499c2713SBiju Das #endif 309499c2713SBiju Das }; 310499c2713SBiju Das 311499c2713SBiju Das int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) 312499c2713SBiju Das { 313499c2713SBiju Das *psci_ops = &rcar_plat_psci_ops; 314499c2713SBiju Das rcar_sec_entrypoint = sec_entrypoint; 315499c2713SBiju Das 316499c2713SBiju Das #if RCAR_SYSTEM_SUSPEND 317499c2713SBiju Das rcar_pwrc_init_suspend_to_ram(); 318499c2713SBiju Das #endif 319499c2713SBiju Das return 0; 320499c2713SBiju Das } 321499c2713SBiju Das 322