blob: ac0f8f7ddeb4d6a073ade78c53b35ac427ad1b31 [file] [log] [blame]
/*
* Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
* Copyright 2017 NXP
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <debug.h>
#include <stdbool.h>
#include <plat_imx8.h>
#include <psci.h>
#include <mmio.h>
#include <soc.h>
#define SCR_A53RCR1_OFFSET 0x08
#define SRC_GPR1_OFFSET 0x74
#define ARM_PGC 0x800
#define PGC_PCR 1
#define GPC_CPU_PGC_SW_PUP_REQ 0xf0
#define GPC_CPU_PGC_SW_PDN_REQ 0xfc
#define SNVS_LPCR 0x38
extern void imx_gpc_set_core_pdn_pup_by_software(unsigned int cpu, bool pdn);
extern void imx_gpc_set_cpu_power_gate_by_wfi(unsigned int cpu, bool pdn);
void imx8m_kill_cpu(unsigned int target_idx)
{
unsigned int val1;
/* Disable the secondary core */
val1 = mmio_read_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET);
val1 &= ~(1 << target_idx);
mmio_write_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET, val1);
imx_gpc_set_core_pdn_pup_by_software(target_idx, true);
}
int imx_pwr_domain_on(u_register_t mpidr)
{
int ret = PSCI_E_SUCCESS;
unsigned int cpu_id, reg;
uint64_t base_addr = BL31_BASE;
cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
/* core power up sequence
* 1. Assert nCPUPORESET
* 2. power the core.
* 3. Deassert reset.
*/
reg = mmio_read_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET);
reg &= ~(1 << cpu_id);
mmio_write_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET, reg);
/* Set CPU jump address */
if (cpu_id > 0) {
base_addr >>= 2;
mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET + (cpu_id << 3),
((uint32_t)(base_addr >> 22) & 0xFFFF));
mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET + (cpu_id << 3) + 4,
((uint32_t)base_addr & 0x003FFFFF));
}
imx_gpc_set_core_pdn_pup_by_software(cpu_id, false);
/* Kick CPU here */
reg = mmio_read_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET);
reg |= (1 << cpu_id);
mmio_write_32(IMX_SRC_BASE + SCR_A53RCR1_OFFSET, reg);
return ret;
}
void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
/* program the GIC per cpu dist and rdist interface */
plat_gic_pcpu_init();
/* enable the GICv3 cpu interface */
plat_gic_cpuif_enable();
}
void imx_pwr_domain_off(const psci_power_state_t *target_state)
{
plat_gic_cpuif_disable();
}
int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
{
/* TODO */
return PSCI_E_SUCCESS;
}
int imx_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
if (pwr_lvl > PLAT_MAX_PWR_LVL)
return PSCI_E_INVALID_PARAMS;
/* Sanity check the requested afflvl */
if (psci_get_pstate_type(power_state) == PSTATE_TYPE_STANDBY) {
if (pwr_lvl != MPIDR_AFFLVL0)
return PSCI_E_INVALID_PARAMS;
/* power domain in standby state */
req_state->pwr_domain_state[pwr_lvl] = PLAT_MAX_RET_STATE;
return PSCI_E_SUCCESS;
}
return 0;
}
void imx_cpu_standby(plat_local_state_t cpu_state)
{
dsb();
write_scr_el3(read_scr_el3() | 0x4);
isb();
wfi();
write_scr_el3(read_scr_el3() & (~0x4));
isb();
}
void imx_domain_suspend(const psci_power_state_t *target_state)
{
uint64_t base_addr = BL31_BASE;
plat_gic_cpuif_disable();
ddrc_enter_retention();
/* imx gpc pre suspend */
imx_gpc_pre_suspend(true);
base_addr >>= 2;
mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET ,
((uint32_t)(base_addr >> 22) & 0xFFFF));
mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET + 4,
((uint32_t)base_addr & 0x003FFFFF));
}
void imx_domain_suspend_finish(const psci_power_state_t *target_state)
{
ddrc_exit_retention();
/* imx gpc post resume */
imx_gpc_post_resume();
/* enable the GICv3 cpu interface */
plat_gic_cpuif_enable();
}
void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
{
unsigned int i;
for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++)
req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE;
}
void __attribute__((noreturn)) imx_system_reset(void)
{
uintptr_t wdog_base = IMX_WDOG_BASE;
unsigned int val;
/* WDOG_B reset */
val = mmio_read_16(wdog_base);
#ifdef IMX_WDOG_B_RESET
val = (val & 0x00FF) | (7 << 2) | (1 << 0);
#else
val = (val & 0x00FF) | (4 << 2) | (1 << 0);
#endif
mmio_write_16(wdog_base, val);
mmio_write_16(wdog_base + 0x2, 0x5555);
mmio_write_16(wdog_base + 0x2, 0xaaaa);
while (1)
;
}
void __attribute__((noreturn)) imx_system_off(void)
{
mmio_write_32(IMX_SNVS_BASE + SNVS_LPCR, 0x61);
INFO("Unable to poweroff system\n");
while (1)
;
}
static const plat_psci_ops_t imx_plat_psci_ops = {
.pwr_domain_on = imx_pwr_domain_on,
.pwr_domain_on_finish = imx_pwr_domain_on_finish,
.pwr_domain_off = imx_pwr_domain_off,
.validate_ns_entrypoint = imx_validate_ns_entrypoint,
.validate_power_state = imx_validate_power_state,
.cpu_standby = imx_cpu_standby,
.pwr_domain_suspend = imx_domain_suspend,
.pwr_domain_suspend_finish = imx_domain_suspend_finish,
.get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
.system_reset = imx_system_reset,
.system_off = imx_system_off,
};
/* export the platform specific psci ops */
int plat_setup_psci_ops(uintptr_t sec_entrypoint,
const plat_psci_ops_t **psci_ops)
{
imx_mailbox_init(sec_entrypoint);
/* sec_entrypoint is used for warm reset */
*psci_ops = &imx_plat_psci_ops;
return 0;
}