diff options
Diffstat (limited to 'plat/marvell/a8k/common/plat_pm.c')
-rw-r--r-- | plat/marvell/a8k/common/plat_pm.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/plat/marvell/a8k/common/plat_pm.c b/plat/marvell/a8k/common/plat_pm.c new file mode 100644 index 00000000..7e5a8d16 --- /dev/null +++ b/plat/marvell/a8k/common/plat_pm.c @@ -0,0 +1,386 @@ +/* + * *************************************************************************** + * Copyright (C) 2016 Marvell International Ltd. + * *************************************************************************** + * + * 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 Marvell 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 <plat_marvell.h> +#include <gicv2.h> +#include <mmio.h> +#include <debug.h> + +#ifdef SCP_IMAGE +#include <bakery_lock.h> +#include <platform.h> +#include <mss_pm_ipc.h> +#include <plat_pm_trace.h> +#endif + +#define MVEBU_PRIVATE_UID_REG 0x30 +#define MVEBU_RFU_GLOBL_SW_RST 0x84 +#define MVEBU_CCU_RVBAR(i) (MVEBU_REGS_BASE + 0x640 + (i * 4)) +#define MVEBU_CCU_CPU_UN_RESET (MVEBU_REGS_BASE + 0x650) + +#define MPIDR_CPU_GET(mpidr) ((mpidr) & MPIDR_CPU_MASK) +#define MPIDR_CLUSTER_GET(mpidr) MPIDR_AFFLVL1_VAL((mpidr)) + +#ifdef SCP_IMAGE +/* this lock synchronize AP multiple cores execution with MSS */ +DEFINE_BAKERY_LOCK(pm_sys_lock); +/* + * this lock ensures core flow execution + * "suspend->suspend finish", and "off-> on finish" + */ +DEFINE_BAKERY_LOCK(pm_core_lock[PLATFORM_CORE_COUNT]); +#endif + +int plat_marvell_cpu_on(u_register_t mpidr) +{ + int cpu_id; + int cluster; + + /* Set barierr */ + __asm__ volatile("dsb sy"); + + /* Get cpu number - use CPU ID */ + cpu_id = MPIDR_CPU_GET(mpidr); + + /* Get cluster number - use affinity level 1 */ + cluster = MPIDR_CLUSTER_GET(mpidr); + + /* Set CPU private UID */ + mmio_write_32(MVEBU_REGS_BASE + MVEBU_PRIVATE_UID_REG, cluster + 0x4); + + /* Set the cpu start address to BL1 entry point (align to 0x10000) */ + mmio_write_32(MVEBU_CCU_RVBAR(0) + (cpu_id << 2), + PLAT_MARVELL_CPU_ENTRY_ADDR >> 16); + + /* Get the cpu out of reset */ + mmio_write_32(MVEBU_CCU_CPU_UN_RESET + (cpu_id << 2), 0x10001); + + return 0; +} + +void plat_marvell_system_reset(void) +{ + mmio_write_32(MVEBU_RFU_BASE + MVEBU_RFU_GLOBL_SW_RST, 0x0); +} + +/******************************************************************************* + * A8K handler called to check the validity of the power state + * parameter. + ******************************************************************************/ +int a8k_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pstate = psci_get_pstate_type(power_state); + int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + int i; + + 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 != MARVELL_PWR_LVL0) + return PSCI_E_INVALID_PARAMS; + + req_state->pwr_domain_state[MARVELL_PWR_LVL0] = + MARVELL_LOCAL_STATE_RET; + } else { + for (i = MARVELL_PWR_LVL0; i <= pwr_lvl; i++) + req_state->pwr_domain_state[i] = + MARVELL_LOCAL_STATE_OFF; + } + + /* + * We expect the 'state id' to be zero. + */ + if (psci_get_pstate_id(power_state)) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * A8K handler called when a CPU is about to enter standby. + ******************************************************************************/ +void a8k_cpu_standby(plat_local_state_t cpu_state) +{ + ERROR("a8k_cpu_standby needs to be implemented\n"); + panic(); +} + +/******************************************************************************* + * A8K handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. + ******************************************************************************/ +int a8k_pwr_domain_on(u_register_t mpidr) +{ +#ifdef SCP_IMAGE + unsigned int target = ((mpidr & 0xFF) + (((mpidr >> 8) & 0xFF) * 2)); + + /* + * pm system synchronization - used to synchronize + * multiple core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* trace message */ + PM_TRACE((TRACE_PWR_DOMAIN_ON | target), plat_my_core_pos()); + + /* trigger IPC message to MSS */ + mss_pm_ipc_on_msg_trigger(target); + + /* verify command execution before continue to ATF generic code */ + __asm__ volatile("dsb sy"); + __asm__ volatile("isb"); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); +#else + /* proprietary CPU ON exection flow */ + plat_marvell_cpu_on(mpidr); +#endif /* SCP_IMAGE */ + + return 0; +} + +/******************************************************************************* + * A8K handler called to validate the entry point. + ******************************************************************************/ +int a8k_validate_ns_entrypoint(uintptr_t entrypoint) +{ + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * A8K handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +void a8k_pwr_domain_off(const psci_power_state_t *target_state) +{ +#ifdef SCP_IMAGE + unsigned int idx = plat_my_core_pos(); + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + /* + * pm system synchronization - used to synchronize + * multiple core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* + * pm core flow synchronization - is used to protect + * core execution flow, lock is + * released in a8k_pwr_domain_on_finish + */ + bakery_lock_get(&pm_core_lock[idx]); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_OFF, idx); + + /* send CPU OFF IPC Message to MSS */ + mss_pm_ipc_msg_send(idx, target_state); + + /* verify command execution before triggering MSS execution */ + __asm__ volatile("isb"); + + /* Trigger IPC message to MSS */ + mss_pm_ipc_off_msg_trigger(idx); + + /* verify command execution before return to ATF generic code */ + __asm__ volatile("dsb sy"); + __asm__ volatile("isb"); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); +#else + INFO("a8k_pwr_domain_off is not supported without SCP\n"); + return; +#endif /* SCP_IMAGE */ +} + +/******************************************************************************* + * A8K handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +void a8k_pwr_domain_suspend(const psci_power_state_t *target_state) +{ +#ifdef SCP_IMAGE + unsigned int idx = plat_my_core_pos(); + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + /* + * pm system synchronization -used to synchronize + * multiple core access to MSS + */ + bakery_lock_get(&pm_sys_lock); + + /* + * pm core flow synchronization - is used to protect + * core execution flow, lock is + * released in a8k_pwr_domain_suspend_finish + */ + bakery_lock_get(&pm_core_lock[idx]); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_SUSPEND, idx); + + /* send CPU Suspend IPC Message to MSS */ + mss_pm_ipc_msg_send(idx, target_state); + + /* verify command execution before triggering MSS execution */ + __asm__ volatile("isb"); + + /* Trigger IPC message to MSS */ + mss_pm_ipc_suspend_msg_trigger(idx); + + /* verify command execution before return to ATF generic code */ + __asm__ volatile("dsb sy"); + __asm__ volatile("isb"); + + /* pm system synchronization */ + bakery_lock_release(&pm_sys_lock); +#else + INFO("a8k_pwr_domain_suspend is not supported without SCP\n"); + return; +#endif /* SCP_IMAGE */ +} + +/******************************************************************************* + * A8K handler called when a power domain has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. + ******************************************************************************/ +void a8k_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ +#ifdef SCP_IMAGE + unsigned int idx = plat_my_core_pos(); +#endif + + /* arch specific configuration */ + psci_arch_init(); + + /* Interrupt initialization */ + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); + +#ifdef SCP_IMAGE + /* + * pm core flow synchronization - is used to protect + * core execution flow, release lock + * taken in a8k_pwr_domain_off + */ + bakery_lock_release(&pm_core_lock[idx]); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_ON_FINISH, idx); +#endif /* SCP_IMAGE */ +} + +/******************************************************************************* + * A8K handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. + * TODO: At the moment we reuse the on finisher and reinitialize the secure + * context. Need to implement a separate suspend finisher. + ******************************************************************************/ +void a8k_pwr_domain_suspend_finish(const psci_power_state_t *target_state) +{ +#ifdef SCP_IMAGE + unsigned int idx = plat_my_core_pos(); + + /* arch specific configuration */ + psci_arch_init(); + + /* Interrupt initialization */ + gicv2_cpuif_enable(); + + /* + * pm core flow synchronization - is used to protect + * core execution flow, release lock + * taken in a8k_pwr_domain_suspend + */ + bakery_lock_release(&pm_core_lock[idx]); + + /* trace message */ + PM_TRACE(TRACE_PWR_DOMAIN_SUSPEND_FINISH, idx); +#else + INFO("a8k_pwr_domain_on_finish is not supported without SCP\n"); + return; +#endif /* SCP_IMAGE */ +} + +/******************************************************************************* + * A8K handlers to shutdown/reboot the system + ******************************************************************************/ +static void __dead2 a8k_system_off(void) +{ + ERROR("a8k_system_off needs to be implemented\n"); + panic(); + wfi(); + ERROR("A8K System Off: operation not handled.\n"); + panic(); +} + +static void __dead2 a8k_system_reset(void) +{ + plat_marvell_system_reset(); + + /* we shouldn't get to this point */ + panic(); +} + +/******************************************************************************* + * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard + * platform layer will take care of registering the handlers with PSCI. + ******************************************************************************/ +const plat_psci_ops_t plat_arm_psci_pm_ops = { + .cpu_standby = a8k_cpu_standby, + .pwr_domain_on = a8k_pwr_domain_on, + .pwr_domain_off = a8k_pwr_domain_off, + .pwr_domain_suspend = a8k_pwr_domain_suspend, + .pwr_domain_on_finish = a8k_pwr_domain_on_finish, + .pwr_domain_suspend_finish = a8k_pwr_domain_suspend_finish, + .system_off = a8k_system_off, + .system_reset = a8k_system_reset, + .validate_power_state = a8k_validate_power_state, + .validate_ns_entrypoint = a8k_validate_ns_entrypoint +}; |