arm-trusted-firmware/plat/hisilicon/hikey960/hikey960_pm.c
Leo Yan cfde1870ca hikey960: Enable system power off callback
On Hikey960 if outputs GPIO176 low level, it can tell PMIC to power off
the whole board.  To avoid resetting the board and stay off, it also
requires the SW2201's three switches 1/2/3 need to be all set to 0.

Since current code doesn't contain complete GPIO modules and misses to
support GPIO176.  This patch adds all known GPIO modules and initialize
GPIO in BL31, and adds system power off callback to use GPIO176 for PMIC
power off operation.

Change-Id: Ia88859b8b7c87c061420ef75f0de3e2768667bb0
Signed-off-by: Leo Yan <leo.yan@linaro.org>
2020-03-02 22:34:21 +08:00

338 lines
8.1 KiB
C

/*
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/arm/cci.h>
#include <drivers/arm/gicv2.h>
#include <drivers/arm/pl011.h>
#include <drivers/arm/pl061_gpio.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/psci/psci.h>
#include <hi3660.h>
#include <hi3660_crg.h>
#include "drivers/pwrc/hisi_pwrc.h"
#include "hikey960_def.h"
#include "hikey960_private.h"
#define CORE_PWR_STATE(state) \
((state)->pwr_domain_state[MPIDR_AFFLVL0])
#define CLUSTER_PWR_STATE(state) \
((state)->pwr_domain_state[MPIDR_AFFLVL1])
#define SYSTEM_PWR_STATE(state) \
((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
#define DMAC_GLB_REG_SEC 0x694
#define AXI_CONF_BASE 0x820
static unsigned int uart_base;
static console_t console;
static uintptr_t hikey960_sec_entrypoint;
static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state)
{
unsigned long scr;
scr = read_scr_el3();
/* Enable Physical IRQ and FIQ to wake the CPU */
write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
/* Add barrier before CPU enter WFI state */
isb();
dsb();
wfi();
/*
* Restore SCR to the original value, synchronisazion of
* scr_el3 is done by eret while el3_exit to save some
* execution cycles.
*/
write_scr_el3(scr);
}
static int hikey960_pwr_domain_on(u_register_t mpidr)
{
unsigned int core = mpidr & MPIDR_CPU_MASK;
unsigned int cluster =
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
int cluster_stat = cluster_is_powered_on(cluster);
hisi_set_cpu_boot_flag(cluster, core);
mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
hikey960_sec_entrypoint >> 2);
if (cluster_stat)
hisi_powerup_core(cluster, core);
else
hisi_powerup_cluster(cluster, core);
return PSCI_E_SUCCESS;
}
static void
hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
gicv2_pcpu_distif_init();
gicv2_cpuif_enable();
}
void hikey960_pwr_domain_off(const psci_power_state_t *target_state)
{
unsigned long mpidr = read_mpidr_el1();
unsigned int core = mpidr & MPIDR_CPU_MASK;
unsigned int cluster =
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
clr_ex();
isb();
dsbsy();
gicv2_cpuif_disable();
hisi_clear_cpu_boot_flag(cluster, core);
hisi_powerdn_core(cluster, core);
/* check if any core is powered up */
if (hisi_test_cpu_down(cluster, core)) {
cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1()));
isb();
dsbsy();
hisi_powerdn_cluster(cluster, core);
}
}
static void __dead2 hikey960_system_off(void)
{
gpio_set_direction(176, GPIO_DIR_OUT);
gpio_set_value(176, GPIO_LEVEL_LOW);
panic();
}
static void __dead2 hikey960_system_reset(void)
{
dsb();
isb();
mdelay(2000);
mmio_write_32(SCTRL_SCPEREN1_REG,
SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS);
mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef);
panic();
}
int hikey960_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
unsigned int pstate = psci_get_pstate_type(power_state);
unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
int i;
assert(req_state);
if (pwr_lvl > PLAT_MAX_PWR_LVL)
return PSCI_E_INVALID_PARAMS;
/* Sanity check the requested state */
if (pstate == PSTATE_TYPE_STANDBY) {
/*
* It's possible to enter standby only on power level 0
* Ignore any other power level.
*/
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;
}
/*
* We expect the 'state id' to be zero.
*/
if (psci_get_pstate_id(power_state))
return PSCI_E_INVALID_PARAMS;
return PSCI_E_SUCCESS;
}
static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint)
{
/*
* Check if the non secure entrypoint lies within the non
* secure DRAM.
*/
if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE)))
return PSCI_E_SUCCESS;
return PSCI_E_INVALID_ADDRESS;
}
static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state)
{
u_register_t mpidr = read_mpidr_el1();
unsigned int core = mpidr & MPIDR_CPU_MASK;
unsigned int cluster =
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
return;
if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
clr_ex();
isb();
dsbsy();
gicv2_cpuif_disable();
hisi_cpuidle_lock(cluster, core);
hisi_set_cpuidle_flag(cluster, core);
hisi_cpuidle_unlock(cluster, core);
mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core),
hikey960_sec_entrypoint >> 2);
hisi_enter_core_idle(cluster, core);
}
/* Perform the common cluster specific operations */
if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
hisi_cpuidle_lock(cluster, core);
hisi_disable_pdc(cluster);
/* check if any core is powered up */
if (hisi_test_pwrdn_allcores(cluster, core)) {
cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
isb();
dsbsy();
/* mask the pdc wakeup irq, then
* enable pdc to power down the core
*/
hisi_pdc_mask_cluster_wakeirq(cluster);
hisi_enable_pdc(cluster);
hisi_cpuidle_unlock(cluster, core);
/* check the SR flag bit to determine
* CLUSTER_IDLE_IPC or AP_SR_IPC to send
*/
if (hisi_test_ap_suspend_flag())
hisi_enter_ap_suspend(cluster, core);
else
hisi_enter_cluster_idle(cluster, core);
} else {
/* enable pdc */
hisi_enable_pdc(cluster);
hisi_cpuidle_unlock(cluster, core);
}
}
}
static void hikey960_sr_dma_reinit(void)
{
unsigned int ctr = 0;
mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3);
/* 1~15 channel is set non_secure */
for (ctr = 1; ctr <= 15; ctr++)
mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40),
(1 << 6) | (1 << 18));
}
static void
hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
{
unsigned long mpidr = read_mpidr_el1();
unsigned int core = mpidr & MPIDR_CPU_MASK;
unsigned int cluster =
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS;
/* Nothing to be done on waking up from retention from CPU level */
if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
return;
hisi_cpuidle_lock(cluster, core);
hisi_clear_cpuidle_flag(cluster, core);
hisi_cpuidle_unlock(cluster, core);
if (hisi_test_ap_suspend_flag()) {
hikey960_sr_dma_reinit();
gicv2_cpuif_enable();
console_pl011_register(uart_base, PL011_UART_CLK_IN_HZ,
PL011_BAUDRATE, &console);
}
hikey960_pwr_domain_on_finish(target_state);
}
static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state)
{
int i;
for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
}
static const plat_psci_ops_t hikey960_psci_ops = {
.cpu_standby = hikey960_pwr_domain_standby,
.pwr_domain_on = hikey960_pwr_domain_on,
.pwr_domain_on_finish = hikey960_pwr_domain_on_finish,
.pwr_domain_off = hikey960_pwr_domain_off,
.pwr_domain_suspend = hikey960_pwr_domain_suspend,
.pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish,
.system_off = hikey960_system_off,
.system_reset = hikey960_system_reset,
.validate_power_state = hikey960_validate_power_state,
.validate_ns_entrypoint = hikey960_validate_ns_entrypoint,
.get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state,
};
int plat_setup_psci_ops(uintptr_t sec_entrypoint,
const plat_psci_ops_t **psci_ops)
{
unsigned int id = 0;
int ret;
ret = hikey960_read_boardid(&id);
if (ret == 0) {
if (id == 5300U)
uart_base = PL011_UART5_BASE;
else
uart_base = PL011_UART6_BASE;
} else {
uart_base = PL011_UART6_BASE;
}
hikey960_sec_entrypoint = sec_entrypoint;
INFO("%s: sec_entrypoint=0x%lx\n", __func__,
(unsigned long)hikey960_sec_entrypoint);
/*
* Initialize PSCI ops struct
*/
*psci_ops = &hikey960_psci_ops;
return 0;
}