From 28b02e2348f92cfb702695c970b893768471392d Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Thu, 1 Jun 2017 15:20:46 +0800 Subject: [PATCH] hikey960: support BL31 Support BL31 on HiKey960 platform. Implement PSCI. Signed-off-by: Leo Yan Signed-off-by: Haojian Zhuang --- .../hikey960/aarch64/hikey960_helpers.S | 53 +++ .../hisilicon/hikey960/drivers/ipc/hisi_ipc.c | 204 +++++++++ .../hikey960/drivers/pwrc/hisi_pwrc.c | 395 ++++++++++++++++++ .../hikey960/drivers/pwrc/hisi_pwrc.h | 57 +++ plat/hisilicon/hikey960/hi3660_mailbox.c | 166 ++++++++ plat/hisilicon/hikey960/hikey960_bl31_setup.c | 129 ++++++ plat/hisilicon/hikey960/hikey960_pm.c | 305 ++++++++++++++ plat/hisilicon/hikey960/hikey960_private.h | 4 + plat/hisilicon/hikey960/hikey960_topology.c | 64 +++ plat/hisilicon/hikey960/include/hisi_ipc.h | 24 ++ plat/hisilicon/hikey960/platform.mk | 13 + 11 files changed, 1414 insertions(+) create mode 100644 plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c create mode 100644 plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c create mode 100644 plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h create mode 100644 plat/hisilicon/hikey960/hi3660_mailbox.c create mode 100644 plat/hisilicon/hikey960/hikey960_bl31_setup.c create mode 100644 plat/hisilicon/hikey960/hikey960_pm.c create mode 100644 plat/hisilicon/hikey960/hikey960_topology.c create mode 100644 plat/hisilicon/hikey960/include/hisi_ipc.h diff --git a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S index 5a0d8b04..2e24416d 100644 --- a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S +++ b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S @@ -6,6 +6,8 @@ #include #include +#include +#include #include "../hikey960_def.h" .globl plat_my_core_pos @@ -14,6 +16,10 @@ .globl plat_crash_console_putc .globl plat_report_exception .globl plat_reset_handler + .globl set_retention_ticks + .globl clr_retention_ticks + .globl clr_ex + .globl nop func plat_my_core_pos mrs x0, mpidr_el1 @@ -132,6 +138,53 @@ func plat_reset_handler ret endfunc plat_reset_handler + /* ----------------------------------------------------- + * void set_retention_ticks(unsigned int val); + * Clobber list : x0 + * ----------------------------------------------------- + */ +func set_retention_ticks + mrs x0, CPUECTLR_EL1 + bic x0, x0, #CPUECTLR_CPU_RET_CTRL_MASK + orr x0, x0, #RETENTION_ENTRY_TICKS_8 + msr CPUECTLR_EL1, x0 + isb + dsb sy + ret +endfunc set_retention_ticks + + /* ----------------------------------------------------- + * void clr_retention_ticks(unsigned int val); + * Clobber list : x0 + * ----------------------------------------------------- + */ +func clr_retention_ticks + mrs x0, CPUECTLR_EL1 + bic x0, x0, #CPUECTLR_CPU_RET_CTRL_MASK + msr CPUECTLR_EL1, x0 + isb + dsb sy + ret +endfunc clr_retention_ticks + + /* ----------------------------------------------------- + * void clrex(void); + * ----------------------------------------------------- + */ +func clr_ex + clrex + ret +endfunc clr_ex + + /* ----------------------------------------------------- + * void nop(void); + * ----------------------------------------------------- + */ +func nop + nop + ret +endfunc nop + .section .rodata.rev_err_str, "aS" plat_err_str: .asciz "\nPlatform exception reporting:" diff --git a/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c b/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c new file mode 100644 index 00000000..8ce1e4ff --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../hikey960_private.h" + +#define IPC_MBX_SOURCE_REG(m) (IPC_BASE + ((m) << 6)) +#define IPC_MBX_DSET_REG(m) (IPC_BASE + ((m) << 6) + 0x04) +#define IPC_MBX_DCLEAR_REG(m) (IPC_BASE + ((m) << 6) + 0x08) +#define IPC_MBX_DSTATUS_REG(m) (IPC_BASE + ((m) << 6) + 0x0C) +#define IPC_MBX_MODE_REG(m) (IPC_BASE + ((m) << 6) + 0x10) +#define IPC_MBX_IMASK_REG(m) (IPC_BASE + ((m) << 6) + 0x14) +#define IPC_MBX_ICLR_REG(m) (IPC_BASE + ((m) << 6) + 0x18) +#define IPC_MBX_SEND_REG(m) (IPC_BASE + ((m) << 6) + 0x1C) +#define IPC_MBX_DATA_REG(m, d) (IPC_BASE + ((m) << 6) + 0x20 + \ + ((d) * 4)) +#define IPC_CPU_IMST_REG(m) (IPC_BASE + ((m) << 3)) +#define IPC_LOCK_REG (IPC_BASE + 0xA00) +#define IPC_ACK_BIT_SHIFT (1 << 7) +#define IPC_UNLOCK_VALUE (0x1ACCE551) + +/********************************************************* + *bit[31:24]:0~AP + *bit[23:16]:0x1~A15, 0x2~A7 + *bit[15:8]:0~ON, 1~OFF + *bit[7:0]:0x3 cpu power mode + *********************************************************/ +#define IPC_CMD_TYPE(src_obj, cluster_obj, is_off, mode) \ + ((src_obj << 24) | (((cluster_obj) + 1) << 16) | (is_off << 8) | (mode)) + +/********************************************************* + *bit[15:8]:0~no idle, 1~idle + *bit[7:0]:cpux + *********************************************************/ + +#define IPC_CMD_PARA(is_idle, cpu) \ + ((is_idle << 8) | (cpu)) + +#define IPC_STATE_IDLE 0x10 + +enum src_id { + SRC_IDLE = 0, + SRC_A15 = 1 << 0, + SRC_A7 = 1 << 1, + SRC_IOM3 = 1 << 2, + SRC_LPM3 = 1 << 3 +}; + +/*lpm3's mailboxs are 13~17*/ +enum lpm3_mbox_id { + LPM3_MBX0 = 13, + LPM3_MBX1, + LPM3_MBX2, + LPM3_MBX3, + LPM3_MBX4, +}; + +static void cpu_relax(void) +{ + volatile int i; + + for (i = 0; i < 10; i++) + nop(); +} + +static inline void +hisi_ipc_clear_ack(enum src_id source, enum lpm3_mbox_id mbox) +{ + unsigned int int_status = 0; + + do { + int_status = mmio_read_32(IPC_MBX_MODE_REG(mbox)); + int_status &= 0xF0; + cpu_relax(); + } while (int_status != IPC_ACK_BIT_SHIFT); + + mmio_write_32(IPC_MBX_ICLR_REG(mbox), source); +} + +static void +hisi_ipc_send_cmd_with_ack(enum src_id source, enum lpm3_mbox_id mbox, + unsigned int cmdtype, unsigned int cmdpara) +{ + unsigned int regval; + unsigned int mask; + unsigned int state; + + mmio_write_32(IPC_LOCK_REG, IPC_UNLOCK_VALUE); + /* wait for idle and occupy */ + do { + state = mmio_read_32(IPC_MBX_MODE_REG(mbox)); + if (state == IPC_STATE_IDLE) { + mmio_write_32(IPC_MBX_SOURCE_REG(mbox), source); + regval = mmio_read_32(IPC_MBX_SOURCE_REG(mbox)); + if (regval == source) + break; + } + cpu_relax(); + + } while (1); + + /* auto answer */ + mmio_write_32(IPC_MBX_MODE_REG(mbox), 0x1); + + mask = (~((int)source | SRC_LPM3) & 0x3F); + /* mask the other cpus */ + mmio_write_32(IPC_MBX_IMASK_REG(mbox), mask); + /* set data */ + mmio_write_32(IPC_MBX_DATA_REG(mbox, 0), cmdtype); + mmio_write_32(IPC_MBX_DATA_REG(mbox, 1), cmdpara); + /* send cmd */ + mmio_write_32(IPC_MBX_SEND_REG(mbox), source); + /* wait ack and clear */ + hisi_ipc_clear_ack(source, mbox); + + /* release mailbox */ + mmio_write_32(IPC_MBX_SOURCE_REG(mbox), source); +} + +void hisi_ipc_pm_on_off(unsigned int core, unsigned int cluster, + enum pm_mode mode) +{ + unsigned int cmdtype = 0; + unsigned int cmdpara = 0; + enum src_id source = SRC_IDLE; + enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); + + cmdtype = IPC_CMD_TYPE(0, cluster, mode, 0x3); + cmdpara = IPC_CMD_PARA(0, core); + source = cluster ? SRC_A7 : SRC_A15; + hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); +} + +void hisi_ipc_pm_suspend(unsigned int core, unsigned int cluster, + unsigned int affinity_level) +{ + unsigned int cmdtype = 0; + unsigned int cmdpara = 0; + enum src_id source = SRC_IDLE; + enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); + + if (affinity_level == 0x3) + cmdtype = IPC_CMD_TYPE(0, -1, 0x1, 0x3 + affinity_level); + else + cmdtype = IPC_CMD_TYPE(0, cluster, 0x1, 0x3 + affinity_level); + + cmdpara = IPC_CMD_PARA(1, core); + source = cluster ? SRC_A7 : SRC_A15; + hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); +} + +void hisi_ipc_psci_system_off(unsigned int core, unsigned int cluster) +{ + unsigned int cmdtype = 0; + unsigned int cmdpara = 0; + enum src_id source = SRC_IDLE; + enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); + + cmdtype = IPC_CMD_TYPE(0, (0x10 - 1), 0x1, 0x0); + cmdpara = IPC_CMD_PARA(0, 0); + source = cluster ? SRC_A7 : SRC_A15; + hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); +} + +void hisi_ipc_psci_system_reset(unsigned int core, unsigned int cluster, + unsigned int cmd_id) +{ + unsigned int cmdtype = 0; + unsigned int cmdpara = 0; + enum src_id source = SRC_IDLE; + enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); + + cmdtype = IPC_CMD_TYPE(0, (0x10 - 1), 0x0, 0x0); + cmdpara = cmd_id; + source = cluster ? SRC_A7 : SRC_A15; + hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); +} + +int hisi_ipc_init(void) +{ + int ret = 0; + enum lpm3_mbox_id i = LPM3_MBX0; + + mmio_write_32(IPC_LOCK_REG, IPC_UNLOCK_VALUE); + for (i = LPM3_MBX0; i <= LPM3_MBX4; i++) { + mmio_write_32(IPC_MBX_MODE_REG(i), 1); + mmio_write_32(IPC_MBX_IMASK_REG(i), + ((int)SRC_IOM3 | (int)SRC_A15 | (int)SRC_A7)); + mmio_write_32(IPC_MBX_ICLR_REG(i), SRC_A7); + } + + return ret; +} diff --git a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c new file mode 100644 index 00000000..f82144a4 --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include <../hikey960_def.h> +#include +#include "hisi_pwrc.h" + + +/* resource lock api */ +#define RES0_LOCK_BASE (SOC_PCTRL_RESOURCE0_LOCK_ADDR(PCTRL_BASE)) +#define RES1_LOCK_BASE (SOC_PCTRL_RESOURCE1_LOCK_ADDR(PCTRL_BASE)) +#define RES2_LOCK_BASE (SOC_PCTRL_RESOURCE2_LOCK_ADDR(PCTRL_BASE)) + +#define LOCK_BIT (0x1 << 28) +#define LOCK_ID_MASK (0x7 << 29) +#define CPUIDLE_LOCK_ID(core) (0x6 - (core)) +#define LOCK_UNLOCK_OFFSET 0x4 +#define LOCK_STAT_OFFSET 0x8 + +#define CLUSTER0_CPUS_ONLINE_MASK (0xF << 16) +#define CLUSTER1_CPUS_ONLINE_MASK (0xF << 20) + +/* cpu hotplug flag api */ +#define SCTRL_BASE (SOC_ACPU_SCTRL_BASE_ADDR) +#define REG_SCBAKDATA3_OFFSET (SOC_SCTRL_SCBAKDATA3_ADDR(SCTRL_BASE)) +#define REG_SCBAKDATA8_OFFSET (SOC_SCTRL_SCBAKDATA8_ADDR(SCTRL_BASE)) +#define REG_SCBAKDATA9_OFFSET (SOC_SCTRL_SCBAKDATA9_ADDR(SCTRL_BASE)) + +#define CPUIDLE_FLAG_REG(cluster) \ + ((cluster == 0) ? REG_SCBAKDATA8_OFFSET : \ + REG_SCBAKDATA9_OFFSET) +#define CLUSTER_IDLE_BIT BIT(8) +#define CLUSTER_IDLE_MASK (CLUSTER_IDLE_BIT | 0x0F) + +#define AP_SUSPEND_FLAG (1 << 16) + +#define CLUSTER_PWDN_IDLE (0<<28) +#define CLUSTER_PWDN_HOTPLUG (1<<28) +#define CLUSTER_PWDN_SR (2<<28) + +#define CLUSTER0_PDC_OFFSET 0x260 +#define CLUSTER1_PDC_OFFSET 0x300 + +#define PDC_EN_OFFSET 0x0 +#define PDC_COREPWRINTEN_OFFSET 0x4 +#define PDC_COREPWRINTSTAT_OFFSET 0x8 +#define PDC_COREGICMASK_OFFSET 0xc +#define PDC_COREPOWERUP_OFFSET 0x10 +#define PDC_COREPOWERDN_OFFSET 0x14 +#define PDC_COREPOWERSTAT_OFFSET 0x18 + +#define PDC_COREPWRSTAT_MASK (0XFFFF) + +enum pdc_gic_mask { + PDC_MASK_GIC_WAKE_IRQ, + PDC_UNMASK_GIC_WAKE_IRQ +}; + +enum pdc_finish_int_mask { + PDC_DISABLE_FINISH_INT, + PDC_ENABLE_FINISH_INT +}; + +static void hisi_resource_lock(unsigned int lockid, unsigned int offset) +{ + unsigned int lock_id = (lockid << 29); + unsigned int lock_val = lock_id | LOCK_BIT; + unsigned int lock_state; + + do { + mmio_write_32(offset, lock_val); + lock_state = mmio_read_32(LOCK_STAT_OFFSET + (uintptr_t)offset); + } while ((lock_state & LOCK_ID_MASK) != lock_id); +} + +static void hisi_resource_unlock(unsigned int lockid, unsigned int offset) +{ + unsigned int lock_val = (lockid << 29) | LOCK_BIT; + + mmio_write_32((LOCK_UNLOCK_OFFSET + (uintptr_t)offset), lock_val); +} + + +static void hisi_cpuhotplug_lock(unsigned int cluster, unsigned int core) +{ + unsigned int lock_id; + + lock_id = (cluster << 2) + core; + + hisi_resource_lock(lock_id, RES2_LOCK_BASE); +} + +static void hisi_cpuhotplug_unlock(unsigned int cluster, unsigned int core) +{ + unsigned int lock_id; + + lock_id = (cluster << 2) + core; + + hisi_resource_unlock(lock_id, RES2_LOCK_BASE); +} + +/* get the resource lock */ +void hisi_cpuidle_lock(unsigned int cluster, unsigned int core) +{ + unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); + + hisi_resource_lock(CPUIDLE_LOCK_ID(core), offset); +} + +/* release the resource lock */ +void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core) +{ + unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); + + hisi_resource_unlock(CPUIDLE_LOCK_ID(core), offset); +} + +unsigned int hisi_get_cpuidle_flag(unsigned int cluster) +{ + unsigned int val; + + val = mmio_read_32(CPUIDLE_FLAG_REG(cluster)); + val &= 0xF; + + return val; +} + +void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core) +{ + mmio_setbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); +} + +void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core) +{ + mmio_clrbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); + +} + +int hisi_test_ap_suspend_flag(unsigned int cluster) +{ + unsigned int val; + + val = mmio_read_32(CPUIDLE_FLAG_REG(cluster)); + val &= AP_SUSPEND_FLAG; + return !!val; +} + +void hisi_set_cluster_pwdn_flag(unsigned int cluster, + unsigned int core, unsigned int value) +{ + unsigned int val; + + hisi_cpuhotplug_lock(cluster, core); + + val = mmio_read_32(REG_SCBAKDATA3_OFFSET); + val = (value << (cluster << 1)) | (val & 0xFFFFFFF); + mmio_write_32(REG_SCBAKDATA3_OFFSET, val); + + hisi_cpuhotplug_unlock(cluster, core); +} + +unsigned int hisi_get_cpu_boot_flag(unsigned int cluster, unsigned int core) +{ + unsigned int val; + + hisi_cpuhotplug_lock(cluster, core); + val = mmio_read_32(REG_SCBAKDATA3_OFFSET); + val = val >> (16 + (cluster << 2)); + val &= 0xF; + hisi_cpuhotplug_unlock(cluster, core); + + return val; +} + +unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core) +{ + unsigned int val; + + hisi_cpuhotplug_lock(cluster, core); + val = mmio_read_32(REG_SCBAKDATA3_OFFSET); + val = val >> (16 + (cluster << 2)); + val &= 0xF; + hisi_cpuhotplug_unlock(cluster, core); + + if (val) + return 0; + else + return 1; +} + +void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core) +{ + unsigned int flag = BIT((cluster<<2) + core + 16); + + hisi_cpuhotplug_lock(cluster, core); + + mmio_setbits_32(REG_SCBAKDATA3_OFFSET, flag); + + hisi_cpuhotplug_unlock(cluster, core); +} + +void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core) +{ + unsigned int flag = BIT((cluster<<2) + core + 16); + + hisi_cpuhotplug_lock(cluster, core); + + mmio_clrbits_32(REG_SCBAKDATA3_OFFSET, flag); + + hisi_cpuhotplug_unlock(cluster, core); +} + +int cluster_is_powered_on(unsigned int cluster) +{ + unsigned int val = mmio_read_32(REG_SCBAKDATA3_OFFSET); + int ret; + + if (cluster == 0) + ret = val & CLUSTER0_CPUS_ONLINE_MASK; + else + ret = val & CLUSTER1_CPUS_ONLINE_MASK; + + return !!ret; +} + +static void *hisi_get_pdc_addr(unsigned int cluster) +{ + void *pdc_base_addr; + uintptr_t addr; + + if (cluster == 0) + addr = SOC_CRGPERIPH_A53_PDCEN_ADDR(CRG_BASE); + else + addr = SOC_CRGPERIPH_MAIA_PDCEN_ADDR(CRG_BASE); + pdc_base_addr = (void *)addr; + + return pdc_base_addr; +} + +static unsigned int hisi_get_pdc_stat(unsigned int cluster) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + unsigned int val; + + val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPOWERSTAT_OFFSET); + + return val; +} + +int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core) +{ + unsigned int mask = 0xf << (core * 4); + unsigned int pdc_stat = hisi_get_pdc_stat(cluster); + unsigned int boot_flag = hisi_get_cpu_boot_flag(cluster, core); + unsigned int cpuidle_flag = hisi_get_cpuidle_flag(cluster); + + mask = (PDC_COREPWRSTAT_MASK & (~mask)); + pdc_stat &= mask; + + if ((boot_flag ^ cpuidle_flag) || pdc_stat) + return 0; + else + return 1; +} + +void hisi_disable_pdc(unsigned int cluster) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + mmio_write_32((uintptr_t)pdc_base_addr, 0x0); +} + +void hisi_enable_pdc(unsigned int cluster) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + mmio_write_32((uintptr_t)pdc_base_addr, 0x1); +} + +static inline void hisi_pdc_set_intmask(void *pdc_base_addr, + unsigned int core, + enum pdc_finish_int_mask intmask) +{ + unsigned int val; + + val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET); + if (intmask == PDC_ENABLE_FINISH_INT) + val |= BIT(core); + else + val &= ~BIT(core); + + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, val); +} + +static inline void hisi_pdc_set_gicmask(void *pdc_base_addr, + unsigned int core, + enum pdc_gic_mask gicmask) +{ + unsigned int val; + + val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET); + if (gicmask == PDC_MASK_GIC_WAKE_IRQ) + val |= BIT(core); + else + val &= ~BIT(core); + + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET, val); +} + +void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster) +{ + int i; + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + for (i = 0; i < 4; i++) + hisi_pdc_set_gicmask(pdc_base_addr, i, PDC_MASK_GIC_WAKE_IRQ); +} + +static void hisi_pdc_powerup_core(unsigned int cluster, unsigned int core, + enum pdc_gic_mask gicmask, + enum pdc_finish_int_mask intmask) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERUP_OFFSET, + BIT(core)); +} + +static void hisi_pdc_powerdn_core(unsigned int cluster, unsigned int core, + enum pdc_gic_mask gicmask, + enum pdc_finish_int_mask intmask) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, + BIT(core)); +} + +void hisi_powerup_core(unsigned int cluster, unsigned int core) +{ + hisi_pdc_powerup_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, + PDC_DISABLE_FINISH_INT); +} + +void hisi_powerdn_core(unsigned int cluster, unsigned int core) +{ + hisi_pdc_powerdn_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, + PDC_DISABLE_FINISH_INT); +} + +void hisi_powerup_cluster(unsigned int cluster, unsigned int core) +{ + hisi_ipc_pm_on_off(core, cluster, PM_ON); +} + +void hisi_powerdn_cluster(unsigned int cluster, unsigned int core) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_HOTPLUG); + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, + (0x10001 << core)); + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, + BIT(core)); +} + +void hisi_enter_core_idle(unsigned int cluster, unsigned int core) +{ + hisi_pdc_powerdn_core(cluster, core, PDC_UNMASK_GIC_WAKE_IRQ, + PDC_DISABLE_FINISH_INT); +} + +void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core) +{ + void *pdc_base_addr = hisi_get_pdc_addr(cluster); + + hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_IDLE); + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, + (0x10001 << core)); + mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, + BIT(core)); +} + +void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core) +{ + hisi_ipc_pm_suspend(core, cluster, 0x3); +} diff --git a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h new file mode 100644 index 00000000..a4d887fb --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __HISI_PWRC_H__ +#define __HISI_PWRC_H__ + +#include +#include + +#define PCTRL_BASE (PCTRL_REG_BASE) +#define CRG_BASE (CRG_REG_BASE) + +#define SOC_CRGPERIPH_A53_PDCEN_ADDR(base) ((base) + (0x260)) +#define SOC_CRGPERIPH_MAIA_PDCEN_ADDR(base) ((base) + (0x300)) + +#define SOC_PCTRL_RESOURCE0_LOCK_ADDR(base) ((base) + (0x400)) +#define SOC_PCTRL_RESOURCE0_UNLOCK_ADDR(base) ((base) + (0x404)) +#define SOC_PCTRL_RESOURCE0_LOCK_ST_ADDR(base) ((base) + (0x408)) +#define SOC_PCTRL_RESOURCE1_LOCK_ADDR(base) ((base) + (0x40C)) +#define SOC_PCTRL_RESOURCE1_UNLOCK_ADDR(base) ((base) + (0x410)) +#define SOC_PCTRL_RESOURCE1_LOCK_ST_ADDR(base) ((base) + (0x414)) +#define SOC_PCTRL_RESOURCE2_LOCK_ADDR(base) ((base) + (0x418)) + +#define SOC_SCTRL_SCBAKDATA3_ADDR(base) ((base) + (0x418)) +#define SOC_SCTRL_SCBAKDATA8_ADDR(base) ((base) + (0x42C)) +#define SOC_SCTRL_SCBAKDATA9_ADDR(base) ((base) + (0x430)) + +#define SOC_ACPU_SCTRL_BASE_ADDR (0xFFF0A000) + +void hisi_cpuidle_lock(unsigned int cluster, unsigned int core); +void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core); +void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core); +void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core); +void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core); +void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core); +int cluster_is_powered_on(unsigned int cluster); +void hisi_enter_core_idle(unsigned int cluster, unsigned int core); +void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core); +int hisi_test_ap_suspend_flag(unsigned int cluster); +void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core); + + +/* pdc api */ +void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster); +int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core); +void hisi_disable_pdc(unsigned int cluster); +void hisi_enable_pdc(unsigned int cluster); +void hisi_powerup_core(unsigned int cluster, unsigned int core); +void hisi_powerdn_core(unsigned int cluster, unsigned int core); +void hisi_powerup_cluster(unsigned int cluster, unsigned int core); +void hisi_powerdn_cluster(unsigned int cluster, unsigned int core); +unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core); + +#endif /* __HISI_PWRC_H__ */ diff --git a/plat/hisilicon/hikey960/hi3660_mailbox.c b/plat/hisilicon/hikey960/hi3660_mailbox.c new file mode 100644 index 00000000..aa12932b --- /dev/null +++ b/plat/hisilicon/hikey960/hi3660_mailbox.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include +#include +#include + +typedef struct hi3660_chan { + unsigned char src; + unsigned char dst; + unsigned char used; +} hi3660_chan_t; + +static hi3660_chan_t chan_map[MBX_MAX_CHANNELS]; + +static void hi3660_mbox_check_state(int chan, unsigned int state) +{ + unsigned int data; + + data = mmio_read_32(MBX_MODE(chan)); + assert((data & (MBX_MODE_AUTO_ANSWER | MBX_MODE_AUTO_LINK)) == 0); + + data &= MBX_MODE_STATE_STATUS_MASK; + assert(data == state); + (void)state; +} + +static int hi3660_mbox_send(int chan, void *message, int len) +{ + int i; + unsigned int *buf; + unsigned int data; + + assert((chan >= 0) && (chan < MBX_MAX_CHANNELS) && + (message != NULL) && (len <= MBX_MAX_DATA_LEN)); + assert((chan_map[chan].used != 0) && + (chan_map[chan].src != 0) && + (chan_map[chan].dst != 0)); + + buf = (unsigned int *)message; + len = ((len + 3) >> 2); /* convert to word count */ + for (i = 0; i < len; i++) + mmio_write_32(MBX_DATA0(chan) + (i << 2), *(buf + i)); + /* send out */ + mmio_write_32(MBX_SEND(chan), chan_map[chan].src); + + do { + data = mmio_read_32(MBX_ICLR(chan)); + } while ((data & chan_map[chan].src) == 0); + /* ack */ + mmio_write_32(MBX_ICLR(chan), chan_map[chan].src); + return 0; +} + +static int hi3660_mbox_recv(int chan, void *message, int *len) +{ + unsigned int *buf, data; + int i; + + assert((chan >= 0) && (chan < MBX_MAX_CHANNELS) && + (message != NULL) && (len != NULL)); + assert((chan_map[chan].used != 0) && + (chan_map[chan].src != 0) && + (chan_map[chan].dst != 0)); + /* wait IPC event */ + do { + data = mmio_read_32(MBX_MODE(chan)); + } while ((data & MBX_MODE_STATE_STATUS_MASK) != MBX_MODE_STATE_DEST); + /* wait to clear interrupt */ + do { + data = mmio_read_32(MBX_ICLR(chan)); + } while (data == 0); + do { + mmio_write_32(MBX_ICLR(chan), chan_map[chan].dst); + data = mmio_read_32(MBX_ICLR(chan)); + } while (data); + + /* read data from IPC */ + buf = (unsigned int *)message; + for (i = 0; i < MBX_MAX_DATA_LEN; i += 4) + *(buf + (i >> 2)) = mmio_read_32(MBX_DATA0(chan) + i); + *len = MBX_MAX_DATA_LEN; + /* ack */ + mmio_write_32(MBX_SEND(chan), chan_map[chan].dst); + return 0; +} + +static int hi3660_mbox_request(int chan, int direction) +{ + unsigned int data; + unsigned int src, dst; + + assert((chan >= 0) && (chan < MBX_MAX_CHANNELS)); + + if (direction == MAILBOX_DIR_TX) { + src = CPU_A53; + dst = CPU_LPM3; + } else if (direction == MAILBOX_DIR_RX) { + src = CPU_LPM3; + dst = CPU_A53; + } else + assert(0); + mmio_write_32(MBX_SOURCE(chan), src); + data = mmio_read_32(MBX_SOURCE(chan)); + assert(data == src); + + /* mask all interrupts */ + mmio_write_32(MBX_IMASK(chan), CPU_MASK); + /* unmask interrupt */ + mmio_write_32(MBX_IMASK(chan), ~(src | dst)); + + /* set destination */ + mmio_write_32(MBX_DCLEAR(chan), (~dst) & CPU_MASK); + mmio_write_32(MBX_DSET(chan), dst); + data = mmio_read_32(MBX_DSTATUS(chan)); + assert((data & dst) != 0); + + /* clear auto link & auto answer */ + data = mmio_read_32(MBX_MODE(chan)); + data &= ~(MBX_MODE_AUTO_ANSWER | MBX_MODE_AUTO_LINK); + mmio_write_32(MBX_MODE(chan), data); + + hi3660_mbox_check_state(chan, MBX_MODE_STATE_SOURCE); + chan_map[chan].used = 1; + chan_map[chan].src = src; + chan_map[chan].dst = dst; + return 0; +} + +static void hi3660_mbox_free(int chan) +{ + assert((chan >= 0) && (chan < MBX_MAX_CHANNELS)); +} + +static mbox_ops_t hi3660_mbox_ops = { + .send = hi3660_mbox_send, + .recv = hi3660_mbox_recv, + .request = hi3660_mbox_request, + .free = hi3660_mbox_free, +}; + +int hi3660_mbox_init(mbox_params_t *params) +{ + int result; + unsigned int data; + + assert(params != NULL); + result = mbox_init(&hi3660_mbox_ops, params); + assert(result == 0); + memset(&chan_map, 0, sizeof(chan_map)); + + /* unlock mailbox */ + data = mmio_read_32(IPC_LOCK); + while (data == MBX_IPC_LOCKED) { + mmio_write_32(IPC_LOCK, MBX_IPC_UNLOCK_MAGIC); + data = mmio_read_32(IPC_LOCK); + } + (void)result; + return 0; +} diff --git a/plat/hisilicon/hikey960/hikey960_bl31_setup.c b/plat/hisilicon/hikey960/hikey960_bl31_setup.c new file mode 100644 index 00000000..41c591b1 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_bl31_setup.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hikey960_def.h" +#include "hikey960_private.h" + +/* + * The next 2 constants identify the extents of the code & RO data region. + * These addresses are used by the MMU setup code and therefore they must be + * page-aligned. It is the responsibility of the linker script to ensure that + * __RO_START__ and __RO_END__ linker symbols refer to page-aligned addresses. + */ +#define BL31_RO_BASE (unsigned long)(&__RO_START__) +#define BL31_RO_LIMIT (unsigned long)(&__RO_END__) + +/* + * The next 2 constants identify the extents of the coherent memory region. + * These addresses are used by the MMU setup code and therefore they must be + * page-aligned. It is the responsibility of the linker script to ensure that + * __COHERENT_RAM_START__ and __COHERENT_RAM_END__ linker symbols refer to + * page-aligned addresses. + */ +#define BL31_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__) +#define BL31_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__) + +static entry_point_info_t bl32_ep_info; +static entry_point_info_t bl33_ep_info; + +/****************************************************************************** + * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 + * interrupts. + *****************************************************************************/ +const unsigned int g0_interrupt_array[] = { + IRQ_SEC_PHY_TIMER, + IRQ_SEC_SGI_0 +}; + +const gicv2_driver_data_t hikey960_gic_data = { + .gicd_base = GICD_REG_BASE, + .gicc_base = GICC_REG_BASE, + .g0_interrupt_num = ARRAY_SIZE(g0_interrupt_array), + .g0_interrupt_array = g0_interrupt_array, +}; + +static const int cci_map[] = { + CCI400_SL_IFACE3_CLUSTER_IX, + CCI400_SL_IFACE4_CLUSTER_IX +}; + +entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) +{ + entry_point_info_t *next_image_info; + + next_image_info = (type == NON_SECURE) ? &bl33_ep_info : &bl32_ep_info; + + /* None of the images on this platform can have 0x0 as the entrypoint */ + if (next_image_info->pc) + return next_image_info; + return NULL; +} + +void bl31_early_platform_setup(bl31_params_t *from_bl2, + void *plat_params_from_bl2) +{ + unsigned int id, uart_base; + + generic_delay_timer_init(); + hikey960_read_boardid(&id); + if (id == 5300) + uart_base = PL011_UART5_BASE; + else + uart_base = PL011_UART6_BASE; + + /* Initialize the console to provide early debug support */ + console_init(uart_base, PL011_UART_CLK_IN_HZ, PL011_BAUDRATE); + + /* Initialize CCI driver */ + cci_init(CCI400_REG_BASE, cci_map, ARRAY_SIZE(cci_map)); + cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); + + /* + * Copy BL3-2 and BL3-3 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ + bl32_ep_info = *from_bl2->bl32_ep_info; + bl33_ep_info = *from_bl2->bl33_ep_info; +} + +void bl31_plat_arch_setup(void) +{ + hikey960_init_mmu_el3(BL31_BASE, + BL31_LIMIT - BL31_BASE, + BL31_RO_BASE, + BL31_RO_LIMIT, + BL31_COHERENT_RAM_BASE, + BL31_COHERENT_RAM_LIMIT); +} + +void bl31_platform_setup(void) +{ + /* Initialize the GIC driver, cpu and distributor interfaces */ + gicv2_driver_init(&hikey960_gic_data); + gicv2_distif_init(); + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); + + hisi_ipc_init(); +} + +void bl31_plat_runtime_setup(void) +{ +} diff --git a/plat/hisilicon/hikey960/hikey960_pm.c b/plat/hisilicon/hikey960/hikey960_pm.c new file mode 100644 index 00000000..257299e8 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_pm.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 uintptr_t hikey960_sec_entrypoint; + +static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state) +{ + unsigned long scr; + unsigned int val = 0; + + assert(cpu_state == PLAT_MAX_RET_STATE); + + scr = read_scr_el3(); + + /* Enable Physical IRQ and FIQ to wake the CPU*/ + write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); + + set_retention_ticks(val); + wfi(); + clr_retention_ticks(val); + + /* + * 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_pwrdn_allcores(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_reset(void) +{ + 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) +{ + int pstate = psci_get_pstate_type(power_state); + 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(cluster)) + 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 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; + + if (hisi_test_ap_suspend_flag(cluster)) { + hikey960_sr_dma_reinit(); + gicv2_cpuif_enable(); + console_init(PL011_UART6_BASE, PL011_UART_CLK_IN_HZ, + PL011_BAUDRATE); + } + + 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 = NULL, + .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) +{ + 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; +} diff --git a/plat/hisilicon/hikey960/hikey960_private.h b/plat/hisilicon/hikey960/hikey960_private.h index 7e95f81d..8f2a842e 100644 --- a/plat/hisilicon/hikey960/hikey960_private.h +++ b/plat/hisilicon/hikey960/hikey960_private.h @@ -26,5 +26,9 @@ void hikey960_init_mmu_el3(unsigned long total_base, unsigned long coh_limit); void hikey960_io_setup(void); int hikey960_read_boardid(unsigned int *id); +void set_retention_ticks(unsigned int val); +void clr_retention_ticks(unsigned int val); +void clr_ex(void); +void nop(void); #endif /* __HIKEY960_PRIVATE_H__ */ diff --git a/plat/hisilicon/hikey960/hikey960_topology.c b/plat/hisilicon/hikey960/hikey960_topology.c new file mode 100644 index 00000000..33637246 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_topology.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include + +/* + * The HiKey power domain tree descriptor. The cluster power domains + * are arranged so that when the PSCI generic code creates the power + * domain tree, the indices of the CPU power domain nodes it allocates + * match the linear indices returned by plat_core_pos_by_mpidr(). + */ +const unsigned char hikey960_power_domain_tree_desc[] = { + /* Number of root nodes */ + 1, + /* Number of clusters */ + PLATFORM_CLUSTER_COUNT, + /* Number of children for the first cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the second cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, +}; + +/******************************************************************************* + * This function returns the HiKey topology tree information. + ******************************************************************************/ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return hikey960_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int cluster_id, cpu_id; + + mpidr &= MPIDR_AFFINITY_MASK; + + if (mpidr & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) + return -1; + + cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) + return -1; + + /* + * Validate cpu_id by checking whether it represents a CPU in + * one of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT_PER_CLUSTER) + return -1; + + return (cpu_id + (cluster_id * 4)); +} diff --git a/plat/hisilicon/hikey960/include/hisi_ipc.h b/plat/hisilicon/hikey960/include/hisi_ipc.h new file mode 100644 index 00000000..9dda1a57 --- /dev/null +++ b/plat/hisilicon/hikey960/include/hisi_ipc.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __HISI_IPC_H__ +#define __HISI_IPC_H__ + +enum pm_mode { + PM_ON = 0, + PM_OFF, +}; + +void hisi_ipc_pm_on_off(unsigned int core, unsigned int cluster, + enum pm_mode mode); +void hisi_ipc_pm_suspend(unsigned int core, unsigned int cluster, + unsigned int affinity_level); +void hisi_ipc_psci_system_off(unsigned int core, unsigned int cluster); +void hisi_ipc_psci_system_reset(unsigned int core, unsigned int cluster, + unsigned int cmd_id); +int hisi_ipc_init(void); + +#endif /* __HISI_IPC_H__ */ diff --git a/plat/hisilicon/hikey960/platform.mk b/plat/hisilicon/hikey960/platform.mk index bb16dfea..145eee0e 100644 --- a/plat/hisilicon/hikey960/platform.mk +++ b/plat/hisilicon/hikey960/platform.mk @@ -50,3 +50,16 @@ BL2_SOURCES += drivers/io/io_block.c \ plat/hisilicon/hikey960/hikey960_bl2_setup.c \ plat/hisilicon/hikey960/hikey960_io_storage.c \ plat/hisilicon/hikey960/hikey960_mcu_load.c + +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a53.S \ + lib/cpus/aarch64/cortex_a72.S \ + lib/cpus/aarch64/cortex_a73.S \ + plat/common/aarch64/plat_psci_common.c \ + plat/hisilicon/hikey960/aarch64/hikey960_helpers.S \ + plat/hisilicon/hikey960/hikey960_bl31_setup.c \ + plat/hisilicon/hikey960/hikey960_pm.c \ + plat/hisilicon/hikey960/hikey960_topology.c \ + plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c \ + plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c \ + ${HIKEY960_GIC_SOURCES} -- 2.30.2