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/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__ */