diff options
Diffstat (limited to '')
-rw-r--r-- | plat/renesas/common/plat_pm.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/plat/renesas/common/plat_pm.c b/plat/renesas/common/plat_pm.c new file mode 100644 index 0000000..9810596 --- /dev/null +++ b/plat/renesas/common/plat_pm.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/arm/cci.h> +#include <drivers/arm/gicv2.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/common/platform.h> + +#include "iic_dvfs.h" +#include "platform_def.h" +#include "pwrc.h" +#include "rcar_def.h" +#include "rcar_private.h" +#if RCAR_GEN3_ULCB +#include "ulcb_cpld.h" +#endif /* RCAR_GEN3_ULCB */ + +#define DVFS_SET_VID_0V (0x00) +#define P_ALL_OFF (0x80) +#define KEEPON_DDR1C (0x08) +#define KEEPON_DDR0C (0x04) +#define KEEPON_DDR1 (0x02) +#define KEEPON_DDR0 (0x01) + +#define SYSTEM_PWR_STATE(s) ((s)->pwr_domain_state[PLAT_MAX_PWR_LVL]) +#define CLUSTER_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL1]) +#define CORE_PWR_STATE(s) ((s)->pwr_domain_state[MPIDR_AFFLVL0]) + +extern void rcar_pwrc_restore_generic_timer(uint64_t *stack); +extern void plat_rcar_gic_driver_init(void); +extern void plat_rcar_gic_init(void); + +static uintptr_t rcar_sec_entrypoint; + +static void rcar_program_mailbox(u_register_t mpidr, uintptr_t address) +{ + mailbox_t *rcar_mboxes = (mailbox_t *) MBOX_BASE; + uint64_t linear_id = plat_core_pos_by_mpidr(mpidr); + unsigned long range; + + rcar_mboxes[linear_id].value = address; + range = (unsigned long)&rcar_mboxes[linear_id]; + + flush_dcache_range(range, sizeof(range)); +} + +static void rcar_cpu_standby(plat_local_state_t cpu_state) +{ + u_register_t scr_el3 = read_scr_el3(); + + write_scr_el3(scr_el3 | SCR_IRQ_BIT); + dsb(); + wfi(); + write_scr_el3(scr_el3); +} + +static int rcar_pwr_domain_on(u_register_t mpidr) +{ + rcar_program_mailbox(mpidr, rcar_sec_entrypoint); + rcar_pwrc_cpuon(mpidr); + + return PSCI_E_SUCCESS; +} + +static void rcar_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + uint32_t cluster_type = rcar_pwrc_get_cluster(); + u_register_t mpidr = read_mpidr_el1(); + + if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) + if (cluster_type == RCAR_CLUSTER_A53A57) + plat_cci_enable(); + + rcar_program_mailbox(mpidr, 0); + rcar_pwrc_enable_interrupt_wakeup(mpidr); + + gicv2_cpuif_enable(); + gicv2_pcpu_distif_init(); +} + +static void rcar_pwr_domain_off(const psci_power_state_t *target_state) +{ +#if RCAR_LSI != RCAR_D3 + uint32_t cluster_type = rcar_pwrc_get_cluster(); +#endif + u_register_t mpidr = read_mpidr_el1(); + + rcar_pwrc_disable_interrupt_wakeup(mpidr); + gicv2_cpuif_disable(); + rcar_pwrc_cpuoff(mpidr); + +#if RCAR_LSI != RCAR_D3 + if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { + if (cluster_type == RCAR_CLUSTER_A53A57) + plat_cci_disable(); + + rcar_pwrc_clusteroff(mpidr); + } +#endif +} + +static void rcar_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + uint32_t cluster_type = rcar_pwrc_get_cluster(); + u_register_t mpidr = read_mpidr_el1(); + + if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) + return; + + rcar_program_mailbox(mpidr, rcar_sec_entrypoint); + rcar_pwrc_enable_interrupt_wakeup(mpidr); + gicv2_cpuif_disable(); + rcar_pwrc_cpuoff(mpidr); + + if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { + if (cluster_type == RCAR_CLUSTER_A53A57) + plat_cci_disable(); + + rcar_pwrc_clusteroff(mpidr); + } +} + +static void rcar_pwr_domain_suspend_finish(const psci_power_state_t + *target_state) +{ + uint32_t cluster_type = rcar_pwrc_get_cluster(); + + if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) + goto finish; + + plat_rcar_gic_driver_init(); + plat_rcar_gic_init(); + + if (cluster_type == RCAR_CLUSTER_A53A57) + plat_cci_init(); + + rcar_pwrc_restore_timer_state(); + rcar_pwrc_setup(); + rcar_pwrc_code_copy_to_system_ram(); + +#if RCAR_SYSTEM_SUSPEND + rcar_pwrc_init_suspend_to_ram(); +#endif +finish: + rcar_pwr_domain_on_finish(target_state); +} + +static void __dead2 rcar_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ +#if RCAR_SYSTEM_SUSPEND + if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) + rcar_pwrc_suspend_to_ram(); +#endif + wfi(); + + ERROR("RCAR Power Down: operation not handled.\n"); + panic(); +} + +static void __dead2 rcar_system_off(void) +{ +#if PMIC_ROHM_BD9571 +#if PMIC_LEVEL_MODE + if (rcar_iic_dvfs_send(PMIC, DVFS_SET_VID, DVFS_SET_VID_0V)) + ERROR("BL3-1:Failed the SYSTEM-OFF.\n"); +#else + if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) + ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); +#endif +#else + u_register_t mpidr = read_mpidr_el1(); + u_register_t cpu = mpidr & 0x0000ffffU; + int32_t rtn_on; + + rtn_on = rcar_pwrc_cpu_on_check(mpidr); + + if (cpu != rcar_boot_mpidr) { + panic(); + } + + if (rtn_on != 0) { + panic(); + } + + rcar_pwrc_cpuoff(mpidr); + rcar_pwrc_clusteroff(mpidr); + +#endif /* PMIC_ROHM_BD9571 */ + wfi(); + ERROR("RCAR System Off: operation not handled.\n"); + panic(); +} + +static void __dead2 rcar_system_reset(void) +{ +#if PMIC_ROHM_BD9571 +#if PMIC_LEVEL_MODE +#if RCAR_SYSTEM_RESET_KEEPON_DDR + uint8_t mode; + int32_t error; + + error = rcar_iic_dvfs_send(PMIC, REG_KEEP10, KEEP10_MAGIC); + if (error) { + ERROR("Failed send KEEP10 magic ret=%d\n", error); + goto done; + } + + error = rcar_iic_dvfs_receive(PMIC, BKUP_MODE_CNT, &mode); + if (error) { + ERROR("Failed receive BKUP_Mode_Cnt ret=%d\n", error); + goto done; + } + + mode |= KEEPON_DDR1C | KEEPON_DDR0C | KEEPON_DDR1 | KEEPON_DDR0; + error = rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, mode); + if (error) { + ERROR("Failed send KEEPON_DDRx ret=%d\n", error); + goto done; + } + + rcar_pwrc_set_suspend_to_ram(); +done: +#else + if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) + ERROR("BL3-1:Failed the SYSTEM-RESET.\n"); +#endif +#else +#if (RCAR_GEN3_ULCB == 1) + rcar_cpld_reset_cpu(); +#endif +#endif +#else + rcar_pwrc_system_reset(); +#endif + wfi(); + + ERROR("RCAR System Reset: operation not handled.\n"); + panic(); +} + +static int rcar_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + unsigned int pstate = psci_get_pstate_type(power_state); + uint32_t i; + + if (pstate == PSTATE_TYPE_STANDBY) { + if (pwr_lvl != MPIDR_AFFLVL0) + return PSCI_E_INVALID_PARAMS; + + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + } else { + for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; + } + + if (psci_get_pstate_id(power_state)) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +#if RCAR_SYSTEM_SUSPEND +static void rcar_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + u_register_t mpidr = read_mpidr_el1() & 0x0000ffffU; + int i; + + if (mpidr != rcar_boot_mpidr) + goto deny; + + for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; + + return; +deny: + /* deny system suspend entry */ + req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PSCI_LOCAL_STATE_RUN; + for (i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; +} +#endif + +static const plat_psci_ops_t rcar_plat_psci_ops = { + .cpu_standby = rcar_cpu_standby, + .pwr_domain_on = rcar_pwr_domain_on, + .pwr_domain_off = rcar_pwr_domain_off, + .pwr_domain_suspend = rcar_pwr_domain_suspend, + .pwr_domain_on_finish = rcar_pwr_domain_on_finish, + .pwr_domain_suspend_finish = rcar_pwr_domain_suspend_finish, + .system_off = rcar_system_off, + .system_reset = rcar_system_reset, + .validate_power_state = rcar_validate_power_state, + .pwr_domain_pwr_down_wfi = rcar_pwr_domain_pwr_down_wfi, +#if RCAR_SYSTEM_SUSPEND + .get_sys_suspend_power_state = rcar_get_sys_suspend_power_state, +#endif +}; + +int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) +{ + *psci_ops = &rcar_plat_psci_ops; + rcar_sec_entrypoint = sec_entrypoint; + +#if RCAR_SYSTEM_SUSPEND + rcar_pwrc_init_suspend_to_ram(); +#endif + return 0; +} + |