310 lines
7.3 KiB
C
310 lines
7.3 KiB
C
/*
|
|
* Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <platform_def.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 "pwrc.h"
|
|
#include "rcar_def.h"
|
|
#include "rcar_private.h"
|
|
#include "ulcb_cpld.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])
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
#if RCAR_LSI != RCAR_D3
|
|
uint32_t cluster_type = rcar_pwrc_get_cluster();
|
|
#endif
|
|
unsigned long mpidr = read_mpidr_el1();
|
|
|
|
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();
|
|
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_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_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
|
|
uint64_t cpu = read_mpidr_el1() & 0x0000ffff;
|
|
int32_t rtn_on;
|
|
|
|
rtn_on = rcar_pwrc_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;
|
|
|
|
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
|
|
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;
|
|
}
|
|
|