aboutsummaryrefslogtreecommitdiff
path: root/plat/renesas/rcar/plat_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/renesas/rcar/plat_pm.c')
-rw-r--r--plat/renesas/rcar/plat_pm.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/plat/renesas/rcar/plat_pm.c b/plat/renesas/rcar/plat_pm.c
new file mode 100644
index 0000000000..84cc47e0d1
--- /dev/null
+++ b/plat/renesas/rcar/plat_pm.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <bakery_lock.h>
+#include <bl_common.h>
+#include <cci.h>
+#include <debug.h>
+#include <errno.h>
+#include <gicv2.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <psci.h>
+
+#include "iic_dvfs.h"
+#include "pwrc.h"
+#include "rcar_def.h"
+#include "rcar_private.h"
+
+#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])
+
+uint64_t rcar_stack_generic_timer[5] __attribute__ ((section("data")));
+
+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);
+extern u_register_t rcar_boot_mpidr;
+
+#if (RCAR_GEN3_ULCB == 1)
+extern void rcar_cpld_reset_cpu(void);
+#endif
+
+static uintptr_t rcar_sec_entrypoint;
+
+static void rcar_program_mailbox(uint64_t mpidr, uint64_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)
+{
+ uint32_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();
+ unsigned long mpidr = read_mpidr_el1();
+
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
+ if (cluster_type == RCAR_CLUSTER_A53A57)
+ plat_cci_enable();
+
+ rcar_pwrc_disable_interrupt_wakeup(mpidr);
+ rcar_program_mailbox(mpidr, 0);
+
+ gicv2_cpuif_enable();
+ gicv2_pcpu_distif_init();
+}
+
+static void rcar_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ uint32_t cluster_type = rcar_pwrc_get_cluster();
+ unsigned long mpidr = read_mpidr_el1();
+
+ 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(const psci_power_state_t *target_state)
+{
+ uint32_t cluster_type = rcar_pwrc_get_cluster();
+ unsigned long 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);
+ }
+
+#if RCAR_SYSTEM_SUSPEND
+ if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
+ rcar_pwrc_suspend_to_ram();
+#endif
+}
+
+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_generic_timer(rcar_stack_generic_timer);
+
+ /* start generic timer */
+ write_cntfrq_el0(plat_get_syscnt_freq2());
+ mmio_write_32(RCAR_CNTC_BASE + CNTCR_OFF, CNTCR_FCREQ(U(0)) | CNTCR_EN);
+ rcar_pwrc_setup();
+
+#if RCAR_SYSTEM_SUSPEND
+ rcar_pwrc_init_suspend_to_ram();
+#endif
+finish:
+ rcar_pwr_domain_on_finish(target_state);
+}
+
+static void __dead2 rcar_system_off(void)
+{
+#if PMIC_ROHM_BD9571
+#if PMIC_LEVEL_MODE
+ rcar_pwrc_code_copy_to_system_ram();
+ if (rcar_iic_dvfs_send(PMIC, DVFS_SET_VID, DVFS_SET_VID_0V))
+ ERROR("BL3-1:Failed the SYSTEM-OFF.\n");
+#else
+ rcar_pwrc_code_copy_to_system_ram();
+ if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF))
+ ERROR("BL3-1:Failed the SYSTEM-RESET.\n");
+#endif
+#else
+ uint64_t cpu = read_mpidr_el1() & 0x0000ffff;
+ int32_t rtn_on;
+
+ rtn_on = cpu_on_check(cpu);
+
+ if (cpu == rcar_boot_mpidr)
+ panic();
+
+ if (rtn_on)
+ panic();
+
+ rcar_pwrc_cpuoff(cpu);
+ rcar_pwrc_clusteroff(cpu);
+
+#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;
+
+ rcar_pwrc_code_copy_to_system_ram();
+ 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 recieve 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
+ rcar_pwrc_code_copy_to_system_ram();
+ 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)
+{
+ unsigned long 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,
+#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;
+}
+