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>
diff --git a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S
index 5a0d8b0..2e24416 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 @@
 	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 0000000..8ce1e4f
--- /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 0000000..f82144a
--- /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 0000000..a4d887f
--- /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 0000000..aa12932
--- /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 0000000..41c591b
--- /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 0000000..257299e
--- /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 7e95f81..8f2a842 100644
--- a/plat/hisilicon/hikey960/hikey960_private.h
+++ b/plat/hisilicon/hikey960/hikey960_private.h
@@ -26,5 +26,9 @@
 			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 0000000..3363724
--- /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 0000000..9dda1a5
--- /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 bb16dfe..145eee0 100644
--- a/plat/hisilicon/hikey960/platform.mk
+++ b/plat/hisilicon/hikey960/platform.mk
@@ -50,3 +50,16 @@
 				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}