summaryrefslogtreecommitdiff
path: root/plat/hisilicon
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@linaro.org>2017-06-01 15:20:46 +0800
committerHaojian Zhuang <haojian.zhuang@linaro.org>2017-06-08 07:33:13 +0800
commit28b02e2348f92cfb702695c970b893768471392d (patch)
tree64b35159a638d54a65060477df7bbe75ab19c396 /plat/hisilicon
parent7cb09cb42e5440beecc119b824800e6047074883 (diff)
hikey960: support BL31
Support BL31 on HiKey960 platform. Implement PSCI. Signed-off-by: Leo Yan <leo.yan@linaro.org> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
Diffstat (limited to 'plat/hisilicon')
-rw-r--r--plat/hisilicon/hikey960/aarch64/hikey960_helpers.S53
-rw-r--r--plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c204
-rw-r--r--plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c395
-rw-r--r--plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h57
-rw-r--r--plat/hisilicon/hikey960/hi3660_mailbox.c166
-rw-r--r--plat/hisilicon/hikey960/hikey960_bl31_setup.c129
-rw-r--r--plat/hisilicon/hikey960/hikey960_pm.c305
-rw-r--r--plat/hisilicon/hikey960/hikey960_private.h4
-rw-r--r--plat/hisilicon/hikey960/hikey960_topology.c64
-rw-r--r--plat/hisilicon/hikey960/include/hisi_ipc.h24
-rw-r--r--plat/hisilicon/hikey960/platform.mk13
11 files changed, 1414 insertions, 0 deletions
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 <arch.h>
#include <asm_macros.S>
+#include <cortex_a53.h>
+#include <cortex_a73.h>
#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 <arch_helpers.h>
+#include <assert.h>
+#include <hi3660.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <hisi_ipc.h>
+#include <debug.h>
+
+#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 <arch_helpers.h>
+#include <assert.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <../hikey960_def.h>
+#include <hisi_ipc.h>
+#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 <hi3660.h>
+#include <hi3660_crg.h>
+
+#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 <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <hi3660_mailbox.h>
+#include <mailbox.h>
+#include <mmio.h>
+#include <string.h>
+
+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 <arch_helpers.h>
+#include <arm_gic.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <cci.h>
+#include <console.h>
+#include <debug.h>
+#include <errno.h>
+#include <generic_delay_timer.h>
+#include <gicv2.h>
+#include <hi3660.h>
+#include <hisi_ipc.h>
+#include <platform_def.h>
+
+#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 <arch_helpers.h>
+#include <assert.h>
+#include <cci.h>
+#include <console.h>
+#include <debug.h>
+#include <gicv2.h>
+#include <hi3660.h>
+#include <hi3660_crg.h>
+#include <mmio.h>
+#include <psci.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 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 <arch.h>
+#include <platform_def.h>
+#include <psci.h>
+
+/*
+ * 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}