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;
+}