summaryrefslogtreecommitdiff
path: root/plat
diff options
context:
space:
mode:
Diffstat (limited to 'plat')
-rw-r--r--plat/arm/board/common/board_css_common.c9
-rw-r--r--plat/arm/board/fvp/platform.mk15
-rw-r--r--plat/arm/board/juno/include/platform_def.h9
-rw-r--r--plat/arm/board/juno/juno_topology.c7
-rw-r--r--plat/arm/css/common/css_common.mk22
-rw-r--r--plat/arm/css/drivers/scmi/scmi.h132
-rw-r--r--plat/arm/css/drivers/scmi/scmi_common.c196
-rw-r--r--plat/arm/css/drivers/scmi/scmi_private.h148
-rw-r--r--plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c84
-rw-r--r--plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c74
-rw-r--r--plat/arm/css/drivers/scp/css_pm_scmi.c385
-rw-r--r--plat/hisilicon/hikey/hikey_bl31_setup.c2
-rw-r--r--plat/hisilicon/hikey/hikey_ddr.c6
-rw-r--r--plat/hisilicon/hikey/hikey_pm.c177
-rw-r--r--plat/hisilicon/hikey/hikey_topology.c6
-rw-r--r--plat/hisilicon/hikey/platform.mk2
-rw-r--r--plat/hisilicon/hikey960/aarch64/hikey960_common.c109
-rw-r--r--plat/hisilicon/hikey960/aarch64/hikey960_helpers.S198
-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_bl1_setup.c705
-rw-r--r--plat/hisilicon/hikey960/hikey960_bl2_setup.c255
-rw-r--r--plat/hisilicon/hikey960/hikey960_bl31_setup.c129
-rw-r--r--plat/hisilicon/hikey960/hikey960_boardid.c172
-rw-r--r--plat/hisilicon/hikey960/hikey960_def.h37
-rw-r--r--plat/hisilicon/hikey960/hikey960_io_storage.c190
-rw-r--r--plat/hisilicon/hikey960/hikey960_mcu_load.c52
-rw-r--r--plat/hisilicon/hikey960/hikey960_pm.c305
-rw-r--r--plat/hisilicon/hikey960/hikey960_private.h34
-rw-r--r--plat/hisilicon/hikey960/hikey960_topology.c64
-rw-r--r--plat/hisilicon/hikey960/include/hi3660.h343
-rw-r--r--plat/hisilicon/hikey960/include/hi3660_crg.h179
-rw-r--r--plat/hisilicon/hikey960/include/hi3660_hkadc.h61
-rw-r--r--plat/hisilicon/hikey960/include/hi3660_mem_map.h20
-rw-r--r--plat/hisilicon/hikey960/include/hisi_ipc.h24
-rw-r--r--plat/hisilicon/hikey960/include/plat_macros.S79
-rw-r--r--plat/hisilicon/hikey960/include/platform_def.h101
-rw-r--r--plat/hisilicon/hikey960/platform.mk65
40 files changed, 5157 insertions, 61 deletions
diff --git a/plat/arm/board/common/board_css_common.c b/plat/arm/board/common/board_css_common.c
index 1758a235..42f754e2 100644
--- a/plat/arm/board/common/board_css_common.c
+++ b/plat/arm/board/common/board_css_common.c
@@ -49,6 +49,15 @@ const mmap_region_t plat_arm_mmap[] = {
ARM_MAP_SHARED_RAM,
V2M_MAP_IOFPGA,
CSS_MAP_DEVICE,
+#if CSS_USE_SCMI_DRIVER
+ /*
+ * The SCMI payload area is currently in the Non Secure SRAM. This is
+ * a potential security risk but this will be resolved once SCP
+ * completely replaces SCPI with SCMI as the only communication
+ * protocol.
+ */
+ CSS_MAP_NSRAM,
+#endif
SOC_CSS_MAP_DEVICE,
{0}
};
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 6a759c5a..d6e8ced4 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -31,13 +31,18 @@ endif
$(eval $(call add_define,FVP_INTERCONNECT_DRIVER))
-# Choose the GIC sources depending upon the how the FVP will be invoked
-ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3)
-FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
+FVP_GICV3_SOURCES := drivers/arm/gic/common/gic_common.c \
drivers/arm/gic/v3/gicv3_main.c \
drivers/arm/gic/v3/gicv3_helpers.c \
plat/common/plat_gicv3.c \
plat/arm/common/arm_gicv3.c
+
+# Choose the GIC sources depending upon the how the FVP will be invoked
+ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3)
+FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES}
+else ifeq (${FVP_USE_GIC_DRIVER},FVP_GIC600)
+FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} \
+ drivers/arm/gic/v3/gic600.c
else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV2)
FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
drivers/arm/gic/v2/gicv2_main.c \
@@ -82,9 +87,11 @@ FVP_CPU_LIBS := lib/cpus/${ARCH}/aem_generic.S
ifeq (${ARCH}, aarch64)
FVP_CPU_LIBS += lib/cpus/aarch64/cortex_a35.S \
lib/cpus/aarch64/cortex_a53.S \
+ lib/cpus/aarch64/cortex_a55.S \
lib/cpus/aarch64/cortex_a57.S \
lib/cpus/aarch64/cortex_a72.S \
- lib/cpus/aarch64/cortex_a73.S
+ lib/cpus/aarch64/cortex_a73.S \
+ lib/cpus/aarch64/cortex_a75.S
else
FVP_CPU_LIBS += lib/cpus/aarch32/cortex_a32.S
endif
diff --git a/plat/arm/board/juno/include/platform_def.h b/plat/arm/board/juno/include/platform_def.h
index 8f038267..68c38ee1 100644
--- a/plat/arm/board/juno/include/platform_def.h
+++ b/plat/arm/board/juno/include/platform_def.h
@@ -74,8 +74,13 @@
#endif
#ifdef IMAGE_BL31
-# define PLAT_ARM_MMAP_ENTRIES 5
-# define MAX_XLAT_TABLES 2
+# if CSS_USE_SCMI_DRIVER
+# define PLAT_ARM_MMAP_ENTRIES 6
+# define MAX_XLAT_TABLES 3
+# else
+# define PLAT_ARM_MMAP_ENTRIES 5
+# define MAX_XLAT_TABLES 2
+# endif
#endif
#ifdef IMAGE_BL32
diff --git a/plat/arm/board/juno/juno_topology.c b/plat/arm/board/juno/juno_topology.c
index d2e0c77e..b9412b1f 100644
--- a/plat/arm/board/juno/juno_topology.c
+++ b/plat/arm/board/juno/juno_topology.c
@@ -51,3 +51,10 @@ unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr)
return (((mpidr) & 0x100) ? JUNO_CLUSTER1_CORE_COUNT :\
JUNO_CLUSTER0_CORE_COUNT);
}
+
+/*
+ * The array mapping platform core position (implemented by plat_my_core_pos())
+ * to the SCMI power domain ID implemented by SCP.
+ */
+const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[PLATFORM_CORE_COUNT] = {
+ 2, 3, 4, 5, 0, 1 };
diff --git a/plat/arm/css/common/css_common.mk b/plat/arm/css/common/css_common.mk
index a3d4513a..c2ae9215 100644
--- a/plat/arm/css/common/css_common.mk
+++ b/plat/arm/css/common/css_common.mk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -8,6 +8,9 @@
# By default, SCP images are needed by CSS platforms.
CSS_LOAD_SCP_IMAGES ?= 1
+# By default, SCMI driver is disabled for CSS platforms
+CSS_USE_SCMI_DRIVER ?= 0
+
PLAT_INCLUDES += -Iinclude/plat/arm/css/common \
-Iinclude/plat/arm/css/common/aarch64
@@ -25,10 +28,18 @@ BL2U_SOURCES += plat/arm/css/common/css_bl2u_setup.c \
plat/arm/css/drivers/scpi/css_scpi.c
BL31_SOURCES += plat/arm/css/common/css_pm.c \
- plat/arm/css/common/css_topology.c \
- plat/arm/css/drivers/scp/css_pm_scpi.c \
+ plat/arm/css/common/css_topology.c
+
+ifeq (${CSS_USE_SCMI_DRIVER},0)
+BL31_SOURCES += plat/arm/css/drivers/scp/css_pm_scpi.c \
plat/arm/css/drivers/scpi/css_mhu.c \
plat/arm/css/drivers/scpi/css_scpi.c
+else
+BL31_SOURCES += plat/arm/css/drivers/scp/css_pm_scmi.c \
+ plat/arm/css/drivers/scmi/scmi_common.c \
+ plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c \
+ plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c
+endif
ifneq (${RESET_TO_BL31},0)
$(error "Using BL31 as the reset vector is not supported on CSS platforms. \
@@ -56,3 +67,8 @@ CSS_DETECT_PRE_1_7_0_SCP := 1
# Process CSS_DETECT_PRE_1_7_0_SCP flag
$(eval $(call assert_boolean,CSS_DETECT_PRE_1_7_0_SCP))
$(eval $(call add_define,CSS_DETECT_PRE_1_7_0_SCP))
+
+# Process CSS_USE_SCMI_DRIVER flag
+$(eval $(call assert_boolean,CSS_USE_SCMI_DRIVER))
+$(eval $(call add_define,CSS_USE_SCMI_DRIVER))
+
diff --git a/plat/arm/css/drivers/scmi/scmi.h b/plat/arm/css/drivers/scmi/scmi.h
new file mode 100644
index 00000000..850402a5
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CSS_SCMI_H__
+#define __CSS_SCMI_H__
+
+#include <bakery_lock.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Supported SCMI Protocol Versions */
+#define SCMI_PWR_DMN_PROTO_VER MAKE_SCMI_VERSION(1, 0)
+#define SCMI_SYS_PWR_PROTO_VER MAKE_SCMI_VERSION(1, 0)
+
+#define GET_SCMI_MAJOR_VER(ver) (((ver) >> 16) & 0xffff)
+#define GET_SCMI_MINOR_VER(ver) ((ver) & 0xffff)
+
+#define MAKE_SCMI_VERSION(maj, min) \
+ ((((maj) & 0xffff) << 16) | ((min) & 0xffff))
+
+/* Macro to check if the driver is compatible with the SCMI version reported */
+#define is_scmi_version_compatible(drv, scmi) \
+ ((GET_SCMI_MAJOR_VER(drv) == GET_SCMI_MAJOR_VER(scmi)) && \
+ (GET_SCMI_MINOR_VER(drv) <= GET_SCMI_MINOR_VER(scmi)))
+
+/* SCMI Protocol identifiers */
+#define SCMI_PWR_DMN_PROTO_ID 0x11
+#define SCMI_SYS_PWR_PROTO_ID 0x12
+
+/* Mandatory messages IDs for all SCMI protocols */
+#define SCMI_PROTO_VERSION_MSG 0x0
+#define SCMI_PROTO_ATTR_MSG 0x1
+#define SCMI_PROTO_MSG_ATTR_MSG 0x2
+
+/* SCMI power domain management protocol message IDs */
+#define SCMI_PWR_STATE_SET_MSG 0x4
+#define SCMI_PWR_STATE_GET_MSG 0x5
+
+/* SCMI system power management protocol message IDs */
+#define SCMI_SYS_PWR_STATE_SET_MSG 0x3
+#define SCMI_SYS_PWR_STATE_GET_MSG 0x4
+
+/* Helper macros for system power management protocol commands */
+
+/*
+ * Macros to describe the bit-fields of the `attribute` of system power domain
+ * protocol PROTOCOL_MSG_ATTRIBUTE message.
+ */
+#define SYS_PWR_ATTR_WARM_RESET_SHIFT 31
+#define SCMI_SYS_PWR_WARM_RESET_SUPPORTED (1U << SYS_PWR_ATTR_WARM_RESET_SHIFT)
+
+#define SYS_PWR_ATTR_SUSPEND_SHIFT 30
+#define SCMI_SYS_PWR_SUSPEND_SUPPORTED (1 << SYS_PWR_ATTR_SUSPEND_SHIFT)
+
+/*
+ * Macros to describe the bit-fields of the `flags` parameter of system power
+ * domain protocol SYSTEM_POWER_STATE_SET message.
+ */
+#define SYS_PWR_SET_GRACEFUL_REQ_SHIFT 0
+#define SCMI_SYS_PWR_GRACEFUL_REQ (1 << SYS_PWR_SET_GRACEFUL_REQ_SHIFT)
+#define SCMI_SYS_PWR_FORCEFUL_REQ (0 << SYS_PWR_SET_GRACEFUL_REQ_SHIFT)
+
+/*
+ * Macros to describe the `system_state` parameter of system power
+ * domain protocol SYSTEM_POWER_STATE_SET message.
+ */
+#define SCMI_SYS_PWR_SHUTDOWN 0x0
+#define SCMI_SYS_PWR_COLD_RESET 0x1
+#define SCMI_SYS_PWR_WARM_RESET 0x2
+#define SCMI_SYS_PWR_POWER_UP 0x3
+#define SCMI_SYS_PWR_SUSPEND 0x4
+
+/* SCMI Error code definitions */
+#define SCMI_E_QUEUED 1
+#define SCMI_E_SUCCESS 0
+#define SCMI_E_NOT_SUPPORTED -1
+#define SCMI_E_INVALID_PARAM -2
+#define SCMI_E_DENIED -3
+#define SCMI_E_NOT_FOUND -4
+#define SCMI_E_OUT_OF_RANGE -5
+#define SCMI_E_BUSY -6
+
+/*
+ * SCMI driver platform information. The details of the doorbell mechanism
+ * can be found in the SCMI specification.
+ */
+typedef struct scmi_channel_plat_info {
+ /* SCMI mailbox memory */
+ uintptr_t scmi_mbx_mem;
+ /* The door bell register address */
+ uintptr_t db_reg_addr;
+ /* The bit mask that need to be preserved when ringing doorbell */
+ uint32_t db_preserve_mask;
+ /* The bit mask that need to be set to ring doorbell */
+ uint32_t db_modify_mask;
+} scmi_channel_plat_info_t;
+
+/*
+ * Structure to represent an SCMI channel.
+ */
+typedef struct scmi_channel {
+ scmi_channel_plat_info_t *info;
+ /* The lock for channel access */
+ bakery_lock_t *lock;
+ /* Indicate whether the channel is initialized */
+ int is_initialized;
+} scmi_channel_t;
+
+/* External Common API */
+void *scmi_init(scmi_channel_t *ch);
+int scmi_proto_msg_attr(void *p, uint32_t proto_id, uint32_t command_id,
+ uint32_t *attr);
+int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version);
+
+/*
+ * Power domain protocol commands. Refer to the SCMI specification for more
+ * details on these commands.
+ */
+int scmi_pwr_state_set(void *p, uint32_t domain_id, uint32_t scmi_pwr_state);
+int scmi_pwr_state_get(void *p, uint32_t domain_id, uint32_t *scmi_pwr_state);
+
+/*
+ * System power management protocol commands. Refer SCMI specification for more
+ * details on these commands.
+ */
+int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state);
+int scmi_sys_pwr_state_get(void *p, uint32_t *system_state);
+
+#endif /* __CSS_SCMI_H__ */
diff --git a/plat/arm/css/drivers/scmi/scmi_common.c b/plat/arm/css/drivers/scmi/scmi_common.c
new file mode 100644
index 00000000..d0051c7a
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi_common.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include "scmi.h"
+#include "scmi_private.h"
+
+/*
+ * Private helper function to get exclusive access to SCMI channel.
+ */
+void scmi_get_channel(scmi_channel_t *ch)
+{
+ assert(ch->lock);
+ bakery_lock_get(ch->lock);
+
+ /* Make sure any previous command has finished */
+ assert(SCMI_IS_CHANNEL_FREE(
+ ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
+}
+
+/*
+ * Private helper function to transfer ownership of channel from AP to SCP.
+ */
+void scmi_send_sync_command(scmi_channel_t *ch)
+{
+ mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+
+ SCMI_MARK_CHANNEL_BUSY(mbx_mem->status);
+
+ /*
+ * Ensure that any write to the SCMI payload area is seen by SCP before
+ * we write to the doorbell register. If these 2 writes were reordered
+ * by the CPU then SCP would read stale payload data
+ */
+ dmbst();
+
+ SCMI_RING_DOORBELL(ch->info->db_reg_addr, ch->info->db_modify_mask,
+ ch->info->db_preserve_mask);
+
+ /*
+ * Ensure that the write to the doorbell register is ordered prior to
+ * checking whether the channel is free.
+ */
+ dmbsy();
+
+ /* Wait for channel to be free */
+ while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status))
+ ;
+
+ /*
+ * Ensure that any read to the SCMI payload area is done after reading
+ * mailbox status. If these 2 reads were reordered then the CPU would
+ * read invalid payload data
+ */
+ dmbld();
+}
+
+/*
+ * Private helper function to release exclusive access to SCMI channel.
+ */
+void scmi_put_channel(scmi_channel_t *ch)
+{
+ /* Make sure any previous command has finished */
+ assert(SCMI_IS_CHANNEL_FREE(
+ ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
+
+ assert(ch->lock);
+ bakery_lock_release(ch->lock);
+}
+
+/*
+ * API to query the SCMI protocol version.
+ */
+int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG,
+ token);
+ mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version);
+ assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to query the protocol message attributes for a SCMI protocol.
+ */
+int scmi_proto_msg_attr(void *p, uint32_t proto_id,
+ uint32_t command_id, uint32_t *attr)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id,
+ SCMI_PROTO_MSG_ATTR_MSG, token);
+ mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr);
+ assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * SCMI Driver initialization API. Returns initialized channel on success
+ * or NULL on error. The return type is an opaque void pointer.
+ */
+void *scmi_init(scmi_channel_t *ch)
+{
+ uint32_t version;
+ int ret;
+
+ assert(ch && ch->info);
+ assert(ch->info->db_reg_addr);
+ assert(ch->info->db_modify_mask);
+ assert(ch->info->db_preserve_mask);
+
+ assert(ch->lock);
+
+ bakery_lock_init(ch->lock);
+
+ ch->is_initialized = 1;
+
+ ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version);
+ if (ret != SCMI_E_SUCCESS) {
+ WARN("SCMI power domain protocol version message failed");
+ goto error;
+ }
+
+ if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) {
+ WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x",
+ version, SCMI_PWR_DMN_PROTO_VER);
+ goto error;
+ }
+
+ VERBOSE("SCMI power domain protocol version 0x%x detected\n", version);
+
+ ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version);
+ if ((ret != SCMI_E_SUCCESS)) {
+ WARN("SCMI system power protocol version message failed");
+ goto error;
+ }
+
+ if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) {
+ WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x",
+ version, SCMI_SYS_PWR_PROTO_VER);
+ goto error;
+ }
+
+ VERBOSE("SCMI system power management protocol version 0x%x detected\n",
+ version);
+
+ INFO("SCMI driver initialized\n");
+
+ return (void *)ch;
+
+error:
+ ch->is_initialized = 0;
+ return NULL;
+}
diff --git a/plat/arm/css/drivers/scmi/scmi_private.h b/plat/arm/css/drivers/scmi/scmi_private.h
new file mode 100644
index 00000000..20e1e9b8
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi_private.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CSS_SCMI_PRIVATE_H__
+#define __CSS_SCMI_PRIVATE_H__
+
+/*
+ * SCMI power domain management protocol message and response lengths. It is
+ * calculated as sum of length in bytes of the message header (4) and payload
+ * area (the number of bytes of parameters or return values in the payload).
+ */
+#define SCMI_PROTO_VERSION_MSG_LEN 4
+#define SCMI_PROTO_VERSION_RESP_LEN 12
+
+#define SCMI_PROTO_MSG_ATTR_MSG_LEN 8
+#define SCMI_PROTO_MSG_ATTR_RESP_LEN 12
+
+#define SCMI_PWR_STATE_SET_MSG_LEN 16
+#define SCMI_PWR_STATE_SET_RESP_LEN 8
+
+#define SCMI_PWR_STATE_GET_MSG_LEN 8
+#define SCMI_PWR_STATE_GET_RESP_LEN 12
+
+#define SCMI_SYS_PWR_STATE_SET_MSG_LEN 12
+#define SCMI_SYS_PWR_STATE_SET_RESP_LEN 8
+
+#define SCMI_SYS_PWR_STATE_GET_MSG_LEN 4
+#define SCMI_SYS_PWR_STATE_GET_RESP_LEN 12
+
+/* SCMI message header format bit field */
+#define SCMI_MSG_ID_SHIFT 0
+#define SCMI_MSG_ID_WIDTH 8
+#define SCMI_MSG_ID_MASK ((1 << SCMI_MSG_ID_WIDTH) - 1)
+
+#define SCMI_MSG_TYPE_SHIFT 8
+#define SCMI_MSG_TYPE_WIDTH 2
+#define SCMI_MSG_TYPE_MASK ((1 << SCMI_MSG_TYPE_WIDTH) - 1)
+
+#define SCMI_MSG_PROTO_ID_SHIFT 10
+#define SCMI_MSG_PROTO_ID_WIDTH 8
+#define SCMI_MSG_PROTO_ID_MASK ((1 << SCMI_MSG_PROTO_ID_WIDTH) - 1)
+
+#define SCMI_MSG_TOKEN_SHIFT 18
+#define SCMI_MSG_TOKEN_WIDTH 10
+#define SCMI_MSG_TOKEN_MASK ((1 << SCMI_MSG_TOKEN_WIDTH) - 1)
+
+
+/* SCMI mailbox flags */
+#define SCMI_FLAG_RESP_POLL 0
+#define SCMI_FLAG_RESP_INT 1
+
+/* SCMI power domain protocol `POWER_STATE_SET` message flags */
+#define SCMI_PWR_STATE_SET_FLAG_SYNC 0
+#define SCMI_PWR_STATE_SET_FLAG_ASYNC 1
+
+/*
+ * Helper macro to create an SCMI message header given protocol, message id
+ * and token.
+ */
+#define SCMI_MSG_CREATE(protocol, msg_id, token) \
+ ((((protocol) & SCMI_MSG_PROTO_ID_MASK) << SCMI_MSG_PROTO_ID_SHIFT) | \
+ (((msg_id) & SCMI_MSG_ID_MASK) << SCMI_MSG_ID_SHIFT) | \
+ (((token) & SCMI_MSG_TOKEN_MASK) << SCMI_MSG_TOKEN_SHIFT))
+
+/* Helper macro to get the token from a SCMI message header */
+#define SCMI_MSG_GET_TOKEN(msg) \
+ (((msg) >> SCMI_MSG_TOKEN_SHIFT) & SCMI_MSG_TOKEN_MASK)
+
+/* SCMI Channel Status bit fields */
+#define SCMI_CH_STATUS_RES0_MASK 0xFFFFFFFE
+#define SCMI_CH_STATUS_FREE_SHIFT 0
+#define SCMI_CH_STATUS_FREE_WIDTH 1
+#define SCMI_CH_STATUS_FREE_MASK ((1 << SCMI_CH_STATUS_FREE_WIDTH) - 1)
+
+/* Helper macros to check and write the channel status */
+#define SCMI_IS_CHANNEL_FREE(status) \
+ (!!(((status) >> SCMI_CH_STATUS_FREE_SHIFT) & SCMI_CH_STATUS_FREE_MASK))
+
+#define SCMI_MARK_CHANNEL_BUSY(status) do { \
+ assert(SCMI_IS_CHANNEL_FREE(status)); \
+ (status) &= ~(SCMI_CH_STATUS_FREE_MASK << \
+ SCMI_CH_STATUS_FREE_SHIFT); \
+ } while (0)
+
+/* Helper macros to copy arguments to the mailbox payload */
+#define SCMI_PAYLOAD_ARG1(payld_arr, arg1) \
+ mmio_write_32((uintptr_t)&payld_arr[0], arg1)
+
+#define SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2) do { \
+ SCMI_PAYLOAD_ARG1(payld_arr, arg1); \
+ mmio_write_32((uintptr_t)&payld_arr[1], arg2); \
+ } while (0)
+
+#define SCMI_PAYLOAD_ARG3(payld_arr, arg1, arg2, arg3) do { \
+ SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2); \
+ mmio_write_32((uintptr_t)&payld_arr[2], arg3); \
+ } while (0)
+
+/* Helper macros to read return values from the mailbox payload */
+#define SCMI_PAYLOAD_RET_VAL1(payld_arr, val1) \
+ (val1) = mmio_read_32((uintptr_t)&payld_arr[0])
+
+#define SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2) do { \
+ SCMI_PAYLOAD_RET_VAL1(payld_arr, val1); \
+ (val2) = mmio_read_32((uintptr_t)&payld_arr[1]); \
+ } while (0)
+
+#define SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3) do { \
+ SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2); \
+ (val3) = mmio_read_32((uintptr_t)&payld_arr[2]); \
+ } while (0)
+
+/* Helper macro to ring doorbell */
+#define SCMI_RING_DOORBELL(addr, modify_mask, preserve_mask) do { \
+ uint32_t db = mmio_read_32(addr) & (preserve_mask); \
+ mmio_write_32(addr, db | (modify_mask)); \
+ } while (0)
+
+/*
+ * Private data structure for representing the mailbox memory layout. Refer
+ * the SCMI specification for more details.
+ */
+typedef struct mailbox_mem {
+ uint32_t res_a; /* Reserved */
+ volatile uint32_t status;
+ uint64_t res_b; /* Reserved */
+ uint32_t flags;
+ volatile uint32_t len;
+ uint32_t msg_header;
+ uint32_t payload[];
+} mailbox_mem_t;
+
+
+/* Private APIs for use within SCMI driver */
+void scmi_get_channel(scmi_channel_t *ch);
+void scmi_send_sync_command(scmi_channel_t *ch);
+void scmi_put_channel(scmi_channel_t *ch);
+
+static inline void validate_scmi_channel(scmi_channel_t *ch)
+{
+ assert(ch && ch->is_initialized);
+ assert(ch->info && ch->info->scmi_mbx_mem);
+}
+
+#endif /* __CSS_SCMI_PRIVATE_H__ */
diff --git a/plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c b/plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c
new file mode 100644
index 00000000..90c5d6bc
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include "scmi.h"
+#include "scmi_private.h"
+
+/*
+ * API to set the SCMI power domain power state.
+ */
+int scmi_pwr_state_set(void *p, uint32_t domain_id,
+ uint32_t scmi_pwr_state)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+
+ /*
+ * Only asynchronous mode of `set power state` command is allowed on
+ * application processors.
+ */
+ uint32_t pwr_state_set_msg_flag = SCMI_PWR_STATE_SET_FLAG_ASYNC;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_SET_MSG, token);
+ mbx_mem->len = SCMI_PWR_STATE_SET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG3(mbx_mem->payload, pwr_state_set_msg_flag,
+ domain_id, scmi_pwr_state);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
+ assert(mbx_mem->len == SCMI_PWR_STATE_SET_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to get the SCMI power domain power state.
+ */
+int scmi_pwr_state_get(void *p, uint32_t domain_id,
+ uint32_t *scmi_pwr_state)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_GET_MSG, token);
+ mbx_mem->len = SCMI_PWR_STATE_GET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG1(mbx_mem->payload, domain_id);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *scmi_pwr_state);
+ assert(mbx_mem->len == SCMI_PWR_STATE_GET_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
diff --git a/plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c b/plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c
new file mode 100644
index 00000000..f6da3941
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include "scmi.h"
+#include "scmi_private.h"
+
+/*
+ * API to set the SCMI system power state
+ */
+int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_SET_MSG, token);
+ mbx_mem->len = SCMI_SYS_PWR_STATE_SET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG2(mbx_mem->payload, flags, system_state);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
+ assert(mbx_mem->len == SCMI_SYS_PWR_STATE_SET_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to get the SCMI system power state
+ */
+int scmi_sys_pwr_state_get(void *p, uint32_t *system_state)
+{
+ mailbox_mem_t *mbx_mem;
+ int token = 0, ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_GET_MSG, token);
+ mbx_mem->len = SCMI_SYS_PWR_STATE_GET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *system_state);
+ assert(mbx_mem->len == SCMI_SYS_PWR_STATE_GET_RESP_LEN);
+ assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
diff --git a/plat/arm/css/drivers/scp/css_pm_scmi.c b/plat/arm/css/drivers/scp/css_pm_scmi.c
new file mode 100644
index 00000000..b4c0a7df
--- /dev/null
+++ b/plat/arm/css/drivers/scp/css_pm_scmi.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <css_def.h>
+#include <css_pm.h>
+#include <debug.h>
+#include <plat_arm.h>
+#include <platform.h>
+#include <string.h>
+#include "../scmi/scmi.h"
+#include "css_scp.h"
+
+/*
+ * This file implements the SCP helper functions using SCMI protocol.
+ */
+
+/*
+ * SCMI power state parameter bit field encoding for ARM CSS platforms.
+ *
+ * 31 20 19 16 15 12 11 8 7 4 3 0
+ * +-------------------------------------------------------------+
+ * | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 |
+ * | | | state | state | state | state |
+ * +-------------------------------------------------------------+
+ *
+ * `Max level` encodes the highest level that has a valid power state
+ * encoded in the power state.
+ */
+#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16
+#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4
+#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \
+ ((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1)
+#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(pwr_state, max_lvl) \
+ (pwr_state) |= ((max_lvl) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK) \
+ << SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT
+#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(pwr_state) \
+ (((pwr_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \
+ & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)
+
+#define SCMI_PWR_STATE_LVL_WIDTH 4
+#define SCMI_PWR_STATE_LVL_MASK \
+ ((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1)
+#define SCMI_SET_PWR_STATE_LVL(pwr_state, lvl, lvl_state) \
+ (pwr_state) |= ((lvl_state) & SCMI_PWR_STATE_LVL_MASK) \
+ << (SCMI_PWR_STATE_LVL_WIDTH * (lvl))
+#define SCMI_GET_PWR_STATE_LVL(pwr_state, lvl) \
+ (((pwr_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (lvl))) & \
+ SCMI_PWR_STATE_LVL_MASK)
+
+/*
+ * The SCMI power state enumeration for a power domain level
+ */
+typedef enum {
+ scmi_power_state_off = 0,
+ scmi_power_state_on = 1,
+ scmi_power_state_sleep = 2,
+} scmi_power_state_t;
+
+/*
+ * This mapping array has to be exported by the platform. Each element at
+ * a given index maps that core to an SCMI power domain.
+ */
+extern uint32_t plat_css_core_pos_to_scmi_dmn_id_map[];
+
+/*
+ * The global handle for invoking the SCMI driver APIs after the driver
+ * has been initialized.
+ */
+void *scmi_handle;
+
+/* The SCMI channel global object */
+static scmi_channel_t scmi_channel;
+
+ARM_INSTANTIATE_LOCK
+
+/*
+ * Helper function to suspend a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_suspend(const psci_power_state_t *target_state)
+{
+ int lvl, ret;
+ uint32_t scmi_pwr_state = 0;
+
+ /* At least power domain level 0 should be specified to be suspended */
+ assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
+ ARM_LOCAL_STATE_OFF);
+
+ /* Check if power down at system power domain level is requested */
+ if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* Issue SCMI command for SYSTEM_SUSPEND */
+ ret = scmi_sys_pwr_state_set(scmi_handle,
+ SCMI_SYS_PWR_FORCEFUL_REQ,
+ SCMI_SYS_PWR_SUSPEND);
+ if (ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI system power domain suspend return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+ return;
+ }
+
+ /*
+ * If we reach here, then assert that power down at system power domain
+ * level is running.
+ */
+ assert(target_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] ==
+ ARM_LOCAL_STATE_RUN);
+
+ /* For level 0, specify `scmi_power_state_sleep` as the power state */
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0,
+ scmi_power_state_sleep);
+
+ for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
+ if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
+ break;
+
+ assert(target_state->pwr_domain_state[lvl] ==
+ ARM_LOCAL_STATE_OFF);
+ /*
+ * Specify `scmi_power_state_off` as power state for higher
+ * levels.
+ */
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_off);
+ }
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ ret = scmi_pwr_state_set(scmi_handle,
+ plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()],
+ scmi_pwr_state);
+
+ if (ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+}
+
+/*
+ * Helper function to turn off a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_off(const psci_power_state_t *target_state)
+{
+ int lvl = 0, ret;
+ uint32_t scmi_pwr_state = 0;
+
+ /* At-least the CPU level should be specified to be OFF */
+ assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
+ ARM_LOCAL_STATE_OFF);
+
+ /* PSCI CPU OFF cannot be used to turn OFF system power domain */
+ assert(target_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] ==
+ ARM_LOCAL_STATE_RUN);
+
+ for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
+ if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
+ break;
+
+ assert(target_state->pwr_domain_state[lvl] ==
+ ARM_LOCAL_STATE_OFF);
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_off);
+ }
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ ret = scmi_pwr_state_set(scmi_handle,
+ plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()],
+ scmi_pwr_state);
+
+ if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+}
+
+/*
+ * Helper function to turn ON a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_on(u_register_t mpidr)
+{
+ int lvl = 0, ret;
+ uint32_t scmi_pwr_state = 0;
+
+ for (; lvl <= PLAT_MAX_PWR_LVL; lvl++)
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_on);
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ ret = scmi_pwr_state_set(scmi_handle,
+ plat_css_core_pos_to_scmi_dmn_id_map[plat_core_pos_by_mpidr(mpidr)],
+ scmi_pwr_state);
+
+ if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+}
+
+/*
+ * Helper function to get the power state of a power domain node as reported
+ * by the SCP.
+ */
+int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level)
+{
+ int ret, cpu_idx;
+ uint32_t scmi_pwr_state = 0, lvl_state;
+
+ /* We don't support get power state at the system power domain level */
+ if ((power_level > PLAT_MAX_PWR_LVL) ||
+ (power_level == CSS_SYSTEM_PWR_DMN_LVL)) {
+ WARN("Invalid power level %u specified for SCMI get power state\n",
+ power_level);
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ cpu_idx = plat_core_pos_by_mpidr(mpidr);
+ assert(cpu_idx > -1);
+
+ ret = scmi_pwr_state_get(scmi_handle,
+ plat_css_core_pos_to_scmi_dmn_id_map[cpu_idx],
+ &scmi_pwr_state);
+
+ if (ret != SCMI_E_SUCCESS) {
+ WARN("SCMI get power state command return 0x%x unexpected\n",
+ ret);
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ /*
+ * Find the maximum power level described in the get power state
+ * command. If it is less than the requested power level, then assume
+ * the requested power level is ON.
+ */
+ if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level)
+ return HW_ON;
+
+ lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level);
+ if (lvl_state == scmi_power_state_on)
+ return HW_ON;
+
+ assert((lvl_state == scmi_power_state_off) ||
+ (lvl_state == scmi_power_state_sleep));
+ return HW_OFF;
+}
+
+/*
+ * Helper function to shutdown the system via SCMI.
+ */
+void __dead2 css_scp_sys_shutdown(void)
+{
+ int ret;
+
+ /*
+ * Disable GIC CPU interface to prevent pending interrupt from waking
+ * up the AP from WFI.
+ */
+ plat_arm_gic_cpuif_disable();
+
+ /*
+ * Issue SCMI command for SYSTEM_SHUTDOWN. First issue a graceful
+ * request and if that fails force the request.
+ */
+ ret = scmi_sys_pwr_state_set(scmi_handle,
+ SCMI_SYS_PWR_FORCEFUL_REQ,
+ SCMI_SYS_PWR_SHUTDOWN);
+ if (ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI system power domain shutdown return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+
+ wfi();
+ ERROR("CSS System Shutdown: operation not handled.\n");
+ panic();
+}
+
+/*
+ * Helper function to reset the system via SCMI.
+ */
+void __dead2 css_scp_sys_reboot(void)
+{
+ int ret;
+
+ /*
+ * Disable GIC CPU interface to prevent pending interrupt from waking
+ * up the AP from WFI.
+ */
+ plat_arm_gic_cpuif_disable();
+
+ /*
+ * Issue SCMI command for SYSTEM_REBOOT. First issue a graceful
+ * request and if that fails force the request.
+ */
+ ret = scmi_sys_pwr_state_set(scmi_handle,
+ SCMI_SYS_PWR_FORCEFUL_REQ,
+ SCMI_SYS_PWR_COLD_RESET);
+ if (ret != SCMI_E_SUCCESS) {
+ ERROR("SCMI system power domain reset return 0x%x unexpected\n",
+ ret);
+ panic();
+ }
+
+ wfi();
+ ERROR("CSS System Reset: operation not handled.\n");
+ panic();
+}
+
+scmi_channel_plat_info_t plat_css_scmi_plat_info = {
+ .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE,
+ .db_reg_addr = PLAT_CSS_MHU_BASE + CSS_SCMI_MHU_DB_REG_OFF,
+ .db_preserve_mask = 0xfffffffd,
+ .db_modify_mask = 0x2,
+};
+
+void plat_arm_pwrc_setup(void)
+{
+ scmi_channel.info = &plat_css_scmi_plat_info;
+ scmi_channel.lock = ARM_LOCK_GET_INSTANCE;
+ scmi_handle = scmi_init(&scmi_channel);
+ if (scmi_handle == NULL) {
+ ERROR("SCMI Initialization failed\n");
+ panic();
+ }
+}
+
+/******************************************************************************
+ * This function overrides the default definition for ARM platforms. Initialize
+ * the SCMI driver, query capability via SCMI and modify the PSCI capability
+ * based on that.
+ *****************************************************************************/
+const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
+{
+ uint32_t msg_attr;
+ int ret;
+
+ assert(scmi_handle);
+
+ /* Check that power domain POWER_STATE_SET message is supported */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_SET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS) {
+ ERROR("Set power state command is not supported by SCMI\n");
+ panic();
+ }
+
+ /*
+ * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support
+ * POWER_STATE_GET message.
+ */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_GET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS)
+ ops->get_node_hw_state = NULL;
+
+ /* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS) {
+ /* System power management operations are not supported */
+ ops->system_off = NULL;
+ ops->system_reset = NULL;
+ ops->get_sys_suspend_power_state = NULL;
+ } else if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) {
+ /*
+ * System power management protocol is available, but it does
+ * not support SYSTEM SUSPEND.
+ */
+ ops->get_sys_suspend_power_state = NULL;
+ }
+
+ return ops;
+}
diff --git a/plat/hisilicon/hikey/hikey_bl31_setup.c b/plat/hisilicon/hikey/hikey_bl31_setup.c
index 9a1114a7..82bd97ed 100644
--- a/plat/hisilicon/hikey/hikey_bl31_setup.c
+++ b/plat/hisilicon/hikey/hikey_bl31_setup.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include <arch_helpers.h>
#include <arm_gic.h>
#include <assert.h>
#include <bl_common.h>
@@ -88,6 +89,7 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2,
/* Initialize CCI driver */
cci_init(CCI400_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.
diff --git a/plat/hisilicon/hikey/hikey_ddr.c b/plat/hisilicon/hikey/hikey_ddr.c
index 6328eb69..ab572eb1 100644
--- a/plat/hisilicon/hikey/hikey_ddr.c
+++ b/plat/hisilicon/hikey/hikey_ddr.c
@@ -385,7 +385,7 @@ static void ddrx_rdet(void)
static void ddrx_wdet(void)
{
- unsigned int data, wdet, zero_bdl, dq[4];
+ unsigned int data, wdet, zero_bdl = 0, dq[4];
int i;
data = mmio_read_32((0xf712c000 + 0x0d0));
@@ -454,11 +454,11 @@ static void ddrx_wdet(void)
for (i = 0; i < 4; i++) {
data = mmio_read_32((0xf712c000 + 0x210 + i * 0x80));
if ((!(data & 0x1f)) || (!(data & 0x1f00)) ||
- (!(data & 0x1f0000)) || (!(data & 0x1f000000)))
+ (!(data & 0x1f0000)) || (!(data & 0x1f000000)))
zero_bdl = 1;
data = mmio_read_32((0xf712c000 + 0x214 + i * 0x80));
if ((!(data & 0x1f)) || (!(data & 0x1f00)) ||
- (!(data & 0x1f0000)) || (!(data & 0x1f000000)))
+ (!(data & 0x1f0000)) || (!(data & 0x1f000000)))
zero_bdl = 1;
data = mmio_read_32((0xf712c000 + 0x218 + i * 0x80));
if (!(data & 0x1f))
diff --git a/plat/hisilicon/hikey/hikey_pm.c b/plat/hisilicon/hikey/hikey_pm.c
index 9b4ef5bf..c796e8a5 100644
--- a/plat/hisilicon/hikey/hikey_pm.c
+++ b/plat/hisilicon/hikey/hikey_pm.c
@@ -15,21 +15,19 @@
#include <hisi_sram_map.h>
#include <mmio.h>
#include <psci.h>
+#include <sp804_delay_timer.h>
#include "hikey_def.h"
-#define HIKEY_CLUSTER_STATE_ON 0
-#define HIKEY_CLUSTER_STATE_OFF 1
+#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])
static uintptr_t hikey_sec_entrypoint;
-/* There're two clusters in HiKey. */
-static int hikey_cluster_state[] = {HIKEY_CLUSTER_STATE_OFF,
- HIKEY_CLUSTER_STATE_OFF};
-
-/*******************************************************************************
- * Handler called when a power domain is about to be turned on. The
- * level and mpidr determine the affinity instance.
- ******************************************************************************/
+
static int hikey_pwr_domain_on(u_register_t mpidr)
{
int cpu, cluster;
@@ -54,14 +52,14 @@ static void hikey_pwr_domain_on_finish(const psci_power_state_t *target_state)
mpidr = read_mpidr();
cluster = MPIDR_AFFLVL1_VAL(mpidr);
cpu = MPIDR_AFFLVL0_VAL(mpidr);
- if (hikey_cluster_state[cluster] == HIKEY_CLUSTER_STATE_OFF) {
- /*
- * Enable CCI coherency for this cluster.
- * No need for locks as no other cpu is active at the moment.
- */
+
+
+ /*
+ * Enable CCI coherency for this cluster.
+ * No need for locks as no other cpu is active at the moment.
+ */
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
- hikey_cluster_state[cluster] = HIKEY_CLUSTER_STATE_ON;
- }
/* Zero the jump address in the mailbox for this cpu */
hisi_pwrc_set_core_bx_addr(cpu, cluster, 0);
@@ -72,35 +70,133 @@ static void hikey_pwr_domain_on_finish(const psci_power_state_t *target_state)
gicv2_cpuif_enable();
}
-/*******************************************************************************
- * Handler called when a power domain is about to be turned off. The
- * target_state encodes the power state that each level should transition to.
- ******************************************************************************/
void hikey_pwr_domain_off(const psci_power_state_t *target_state)
{
unsigned long mpidr;
int cpu, cluster;
- gicv2_cpuif_disable();
-
mpidr = read_mpidr();
cluster = MPIDR_AFFLVL1_VAL(mpidr);
cpu = MPIDR_AFFLVL0_VAL(mpidr);
- if (target_state->pwr_domain_state[MPIDR_AFFLVL1] ==
- PLAT_MAX_OFF_STATE) {
+
+ gicv2_cpuif_disable();
+ hisi_ipc_cpu_off(cpu, cluster);
+
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE);
cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE);
hisi_ipc_cluster_off(cpu, cluster);
- hikey_cluster_state[cluster] = HIKEY_CLUSTER_STATE_OFF;
}
- hisi_ipc_cpu_off(cpu, cluster);
}
-/*******************************************************************************
- * Handler to reboot the system.
- ******************************************************************************/
+static void hikey_pwr_domain_suspend(const psci_power_state_t *target_state)
+{
+ u_register_t mpidr = read_mpidr_el1();
+ unsigned int cpu = 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) {
+
+ /* Program the jump address for the target cpu */
+ hisi_pwrc_set_core_bx_addr(cpu, cluster, hikey_sec_entrypoint);
+
+ gicv2_cpuif_disable();
+
+ if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
+ hisi_ipc_cpu_suspend(cpu, cluster);
+ }
+
+ /* Perform the common cluster specific operations */
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+ hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE);
+ cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
+ hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE);
+
+ if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+ hisi_pwrc_set_cluster_wfi(1);
+ hisi_pwrc_set_cluster_wfi(0);
+ hisi_ipc_psci_system_off();
+ } else
+ hisi_ipc_cluster_suspend(cpu, cluster);
+ }
+}
+
+static void hikey_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
+{
+ unsigned long mpidr;
+ unsigned int cluster, cpu;
+
+ /* Nothing to be done on waking up from retention from CPU level */
+ if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
+ return;
+
+ /* Get the mpidr for this cpu */
+ mpidr = read_mpidr_el1();
+ cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT;
+ cpu = mpidr & MPIDR_CPU_MASK;
+
+ /* Enable CCI coherency for cluster */
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
+ cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
+
+ hisi_pwrc_set_core_bx_addr(cpu, cluster, 0);
+
+ if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+ gicv2_distif_init();
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+ } else {
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+ }
+}
+
+static void hikey_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 void __dead2 hikey_system_off(void)
+{
+ NOTICE("%s: off system\n", __func__);
+
+ /* Pull down GPIO_0_0 to trigger PMIC shutdown */
+ mmio_write_32(0xF8001810, 0x2); /* Pinmux */
+ mmio_write_8(0xF8011400, 1); /* Pin direction */
+ mmio_write_8(0xF8011004, 0); /* Pin output value */
+
+ /* Wait for 2s to power off system by PMIC */
+ sp804_timer_init(SP804_TIMER0_BASE, 10, 192);
+ mdelay(2000);
+
+ /*
+ * PMIC shutdown depends on two conditions: GPIO_0_0 (PWR_HOLD) low,
+ * and VBUS_DET < 3.6V. For HiKey, VBUS_DET is connected to VDD_4V2
+ * through Jumper 1-2. So, to complete shutdown, user needs to manually
+ * remove Jumper 1-2.
+ */
+ NOTICE("+------------------------------------------+\n");
+ NOTICE("| IMPORTANT: Remove Jumper 1-2 to shutdown |\n");
+ NOTICE("| DANGER: SoC is still burning. DANGER! |\n");
+ NOTICE("| Board will be reboot to avoid overheat |\n");
+ NOTICE("+------------------------------------------+\n");
+
+ /* Send the system reset request */
+ mmio_write_32(AO_SC_SYS_STAT0, 0x48698284);
+
+ wfi();
+ panic();
+}
+
static void __dead2 hikey_system_reset(void)
{
/* Send the system reset request */
@@ -112,9 +208,6 @@ static void __dead2 hikey_system_reset(void)
panic();
}
-/*******************************************************************************
- * Handler called to check the validity of the power state parameter.
- ******************************************************************************/
int hikey_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
@@ -153,9 +246,6 @@ int hikey_validate_power_state(unsigned int power_state,
return PSCI_E_SUCCESS;
}
-/*******************************************************************************
- * Handler called to check the validity of the non secure entrypoint.
- ******************************************************************************/
static int hikey_validate_ns_entrypoint(uintptr_t entrypoint)
{
/*
@@ -168,26 +258,20 @@ static int hikey_validate_ns_entrypoint(uintptr_t entrypoint)
return PSCI_E_INVALID_ADDRESS;
}
-/*******************************************************************************
- * Export the platform handlers to enable psci to invoke them
- ******************************************************************************/
static const plat_psci_ops_t hikey_psci_ops = {
.cpu_standby = NULL,
.pwr_domain_on = hikey_pwr_domain_on,
.pwr_domain_on_finish = hikey_pwr_domain_on_finish,
.pwr_domain_off = hikey_pwr_domain_off,
- .pwr_domain_suspend = NULL,
- .pwr_domain_suspend_finish = NULL,
- .system_off = NULL,
+ .pwr_domain_suspend = hikey_pwr_domain_suspend,
+ .pwr_domain_suspend_finish = hikey_pwr_domain_suspend_finish,
+ .system_off = hikey_system_off,
.system_reset = hikey_system_reset,
.validate_power_state = hikey_validate_power_state,
.validate_ns_entrypoint = hikey_validate_ns_entrypoint,
- .get_sys_suspend_power_state = NULL,
+ .get_sys_suspend_power_state = hikey_get_sys_suspend_power_state,
};
-/*******************************************************************************
- * Export the platform specific power ops and initialize Power Controller
- ******************************************************************************/
int plat_setup_psci_ops(uintptr_t sec_entrypoint,
const plat_psci_ops_t **psci_ops)
{
@@ -197,6 +281,5 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint,
* Initialize PSCI ops struct
*/
*psci_ops = &hikey_psci_ops;
-
return 0;
}
diff --git a/plat/hisilicon/hikey/hikey_topology.c b/plat/hisilicon/hikey/hikey_topology.c
index 37ea20ad..95948b8d 100644
--- a/plat/hisilicon/hikey/hikey_topology.c
+++ b/plat/hisilicon/hikey/hikey_topology.c
@@ -18,8 +18,10 @@ const unsigned char hikey_power_domain_tree_desc[] = {
1,
/* Number of clusters */
PLATFORM_CLUSTER_COUNT,
- /* Number of CPU cores */
- PLATFORM_CORE_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,
};
/*******************************************************************************
diff --git a/plat/hisilicon/hikey/platform.mk b/plat/hisilicon/hikey/platform.mk
index e0d0a702..85dc40d1 100644
--- a/plat/hisilicon/hikey/platform.mk
+++ b/plat/hisilicon/hikey/platform.mk
@@ -65,6 +65,8 @@ HIKEY_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
plat/common/plat_gicv2.c
BL31_SOURCES += drivers/arm/cci/cci.c \
+ drivers/arm/sp804/sp804_delay_timer.c \
+ drivers/delay_timer/delay_timer.c \
lib/cpus/aarch64/cortex_a53.S \
plat/common/aarch64/plat_psci_common.c \
plat/hisilicon/hikey/aarch64/hikey_helpers.S \
diff --git a/plat/hisilicon/hikey960/aarch64/hikey960_common.c b/plat/hisilicon/hikey960/aarch64/hikey960_common.c
new file mode 100644
index 00000000..d7894b3e
--- /dev/null
+++ b/plat/hisilicon/hikey960/aarch64/hikey960_common.c
@@ -0,0 +1,109 @@
+/*
+ * 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 <debug.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <xlat_tables.h>
+
+#include "../hikey960_def.h"
+#include "../hikey960_private.h"
+
+#define MAP_DDR MAP_REGION_FLAT(DDR_BASE, \
+ DDR_SIZE, \
+ MT_MEMORY | MT_RW | MT_NS)
+
+#define MAP_DEVICE MAP_REGION_FLAT(DEVICE_BASE, \
+ DEVICE_SIZE, \
+ MT_DEVICE | MT_RW | MT_SECURE)
+
+#define MAP_BL1_RW MAP_REGION_FLAT(BL1_RW_BASE, \
+ BL1_RW_LIMIT - BL1_RW_BASE, \
+ MT_MEMORY | MT_RW | MT_NS)
+
+#define MAP_UFS_DATA MAP_REGION_FLAT(HIKEY960_UFS_DATA_BASE, \
+ HIKEY960_UFS_DATA_SIZE, \
+ MT_MEMORY | MT_RW | MT_NS)
+
+#define MAP_UFS_DESC MAP_REGION_FLAT(HIKEY960_UFS_DESC_BASE, \
+ HIKEY960_UFS_DESC_SIZE, \
+ MT_MEMORY | MT_RW | MT_NS)
+
+/*
+ * Table of regions for different BL stages to map using the MMU.
+ * This doesn't include Trusted RAM as the 'mem_layout' argument passed to
+ * hikey960_init_mmu_elx() will give the available subset of that,
+ */
+#if IMAGE_BL1
+static const mmap_region_t hikey960_mmap[] = {
+ MAP_UFS_DATA,
+ MAP_BL1_RW,
+ MAP_UFS_DESC,
+ MAP_DEVICE,
+ {0}
+};
+#endif
+
+#if IMAGE_BL2
+static const mmap_region_t hikey960_mmap[] = {
+ MAP_DDR,
+ MAP_DEVICE,
+ {0}
+};
+#endif
+
+#if IMAGE_BL31
+static const mmap_region_t hikey960_mmap[] = {
+ MAP_DEVICE,
+ {0}
+};
+#endif
+
+/*
+ * Macro generating the code for the function setting up the pagetables as per
+ * the platform memory map & initialize the mmu, for the given exception level
+ */
+#define HIKEY960_CONFIGURE_MMU_EL(_el) \
+ void hikey960_init_mmu_el##_el(unsigned long total_base, \
+ unsigned long total_size, \
+ unsigned long ro_start, \
+ unsigned long ro_limit, \
+ unsigned long coh_start, \
+ unsigned long coh_limit) \
+ { \
+ mmap_add_region(total_base, total_base, \
+ total_size, \
+ MT_MEMORY | MT_RW | MT_SECURE); \
+ mmap_add_region(ro_start, ro_start, \
+ ro_limit - ro_start, \
+ MT_MEMORY | MT_RO | MT_SECURE); \
+ mmap_add_region(coh_start, coh_start, \
+ coh_limit - coh_start, \
+ MT_DEVICE | MT_RW | MT_SECURE); \
+ mmap_add(hikey960_mmap); \
+ init_xlat_tables(); \
+ \
+ enable_mmu_el##_el(0); \
+ }
+
+/* Define EL1 and EL3 variants of the function initialising the MMU */
+HIKEY960_CONFIGURE_MMU_EL(1)
+HIKEY960_CONFIGURE_MMU_EL(3)
+
+unsigned long plat_get_ns_image_entrypoint(void)
+{
+ return NS_BL1U_BASE;
+}
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+ return 1920000;
+}
diff --git a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S
new file mode 100644
index 00000000..2e24416d
--- /dev/null
+++ b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <cortex_a53.h>
+#include <cortex_a73.h>
+#include "../hikey960_def.h"
+
+ .globl plat_my_core_pos
+ .globl platform_mem_init
+ .globl plat_crash_console_init
+ .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
+ and x1, x0, #MPIDR_CPU_MASK
+ and x0, x0, #MPIDR_CLUSTER_MASK
+ add x0, x1, x0, LSR #6
+ ret
+endfunc plat_my_core_pos
+
+ /* -----------------------------------------------------
+ * void platform_mem_init(void);
+ *
+ * We don't need to carry out any memory initialization
+ * on HIKEY. The Secure RAM is accessible straight away.
+ * -----------------------------------------------------
+ */
+func platform_mem_init
+ ret
+endfunc platform_mem_init
+
+ /* ---------------------------------------------
+ * int plat_crash_console_init(void)
+ * Function to initialize the crash console
+ * without a C Runtime to print crash report.
+ * Clobber list : x0, x1, x2
+ * ---------------------------------------------
+ */
+func plat_crash_console_init
+ mov_imm x0, CRASH_CONSOLE_BASE
+ mov_imm x1, PL011_UART_CLK_IN_HZ
+ mov_imm x2, PL011_BAUDRATE
+ b console_core_init
+endfunc plat_crash_console_init
+
+ /* ---------------------------------------------
+ * int plat_crash_console_putc(int c)
+ * Function to print a character on the crash
+ * console without a C Runtime.
+ * Clobber list : x1, x2
+ * ---------------------------------------------
+ */
+func plat_crash_console_putc
+ mov_imm x1, CRASH_CONSOLE_BASE
+ b console_core_putc
+endfunc plat_crash_console_putc
+
+ /* ---------------------------------------------
+ * void plat_report_exception(unsigned int type)
+ * Function to report an unhandled exception
+ * with platform-specific means.
+ * On HIKEY platform, it updates the LEDs
+ * to indicate where we are
+ * ---------------------------------------------
+ */
+func plat_report_exception
+ mov x8, x30
+
+ /* Turn on LED according to x0 (0 -- f) */
+ ldr x2, =0xf7020000
+ and x1, x0, #1
+ str w1, [x2, #4]
+ and x1, x0, #2
+ str w1, [x2, #8]
+ and x1, x0, #4
+ str w1, [x2, #16]
+ and x1, x0, #8
+ str w1, [x2, #32]
+
+ mrs x2, currentel
+ and x2, x2, #0x0c
+ /* Check EL1 */
+ cmp x2, #0x04
+ beq plat_report_el1
+
+ adr x4, plat_err_str
+ bl asm_print_str
+
+ adr x4, esr_el3_str
+ bl asm_print_str
+
+ mrs x4, esr_el3
+ bl asm_print_hex
+
+ adr x4, elr_el3_str
+ bl asm_print_str
+
+ mrs x4, elr_el3
+ bl asm_print_hex
+ b plat_report_end
+
+plat_report_el1:
+ adr x4, plat_err_str
+ bl asm_print_str
+
+ adr x4, esr_el1_str
+ bl asm_print_str
+
+ mrs x4, esr_el1
+ bl asm_print_hex
+
+ adr x4, elr_el1_str
+ bl asm_print_str
+
+ mrs x4, elr_el1
+ bl asm_print_hex
+plat_report_end:
+ mov x30, x8
+ ret
+endfunc plat_report_exception
+
+ /* -----------------------------------------------------
+ * void plat_reset_handler(void);
+ * -----------------------------------------------------
+ */
+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:"
+esr_el3_str:
+ .asciz "\nESR_EL3: "
+elr_el3_str:
+ .asciz "\nELR_EL3: "
+esr_el1_str:
+ .asciz "\nESR_EL1: "
+elr_el1_str:
+ .asciz "\nELR_EL1: "
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_bl1_setup.c b/plat/hisilicon/hikey960/hikey960_bl1_setup.c
new file mode 100644
index 00000000..f9666df1
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_bl1_setup.c
@@ -0,0 +1,705 @@
+/*
+ * 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 <console.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <dw_ufs.h>
+#include <errno.h>
+#include <gicv2.h>
+#include <hi3660.h>
+#include <mmio.h>
+#include <generic_delay_timer.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <string.h>
+#include <tbbr/tbbr_img_desc.h>
+#include <ufs.h>
+
+#include "../../bl1/bl1_private.h"
+#include "hikey960_def.h"
+#include "hikey960_private.h"
+
+enum {
+ BOOT_MODE_RECOVERY = 0,
+ BOOT_MODE_NORMAL,
+ BOOT_MODE_MASK = 1,
+};
+
+/*
+ * Declarations of linker defined symbols which will help us find the layout
+ * of trusted RAM
+ */
+extern unsigned long __COHERENT_RAM_START__;
+extern unsigned long __COHERENT_RAM_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 BL1_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__)
+#define BL1_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__)
+
+/* Data structure which holds the extents of the trusted RAM for BL1 */
+static meminfo_t bl1_tzram_layout;
+
+/******************************************************************************
+ * 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,
+};
+
+meminfo_t *bl1_plat_sec_mem_layout(void)
+{
+ return &bl1_tzram_layout;
+}
+
+/*
+ * Perform any BL1 specific platform actions.
+ */
+void bl1_early_platform_setup(void)
+{
+ const size_t bl1_size = BL1_RAM_LIMIT - BL1_RAM_BASE;
+ 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);
+
+ /* Allow BL1 to see the whole Trusted RAM */
+ bl1_tzram_layout.total_base = BL1_RW_BASE;
+ bl1_tzram_layout.total_size = BL1_RW_SIZE;
+
+ /* Calculate how much RAM BL1 is using and how much remains free */
+ bl1_tzram_layout.free_base = BL1_RW_BASE;
+ bl1_tzram_layout.free_size = BL1_RW_SIZE;
+ reserve_mem(&bl1_tzram_layout.free_base,
+ &bl1_tzram_layout.free_size,
+ BL1_RAM_BASE,
+ bl1_size);
+
+ INFO("BL1: 0x%lx - 0x%lx [size = %lu]\n", BL1_RAM_BASE, BL1_RAM_LIMIT,
+ bl1_size);
+}
+
+/*
+ * Perform the very early platform specific architecture setup here. At the
+ * moment this only does basic initialization. Later architectural setup
+ * (bl1_arch_setup()) does not do anything platform specific.
+ */
+void bl1_plat_arch_setup(void)
+{
+ hikey960_init_mmu_el3(bl1_tzram_layout.total_base,
+ bl1_tzram_layout.total_size,
+ BL1_RO_BASE,
+ BL1_RO_LIMIT,
+ BL1_COHERENT_RAM_BASE,
+ BL1_COHERENT_RAM_LIMIT);
+}
+
+static void hikey960_clk_init(void)
+{
+ /* change ldi0 sel to ppll2 */
+ mmio_write_32(0xfff350b4, 0xf0002000);
+ /* ldi0 20' */
+ mmio_write_32(0xfff350bc, 0xfc004c00);
+}
+
+static void hikey960_pmu_init(void)
+{
+ /* clear np_xo_abb_dig_START bit in PMIC_CLK_TOP_CTRL7 register */
+ mmio_clrbits_32(PMU_SSI0_CLK_TOP_CTRL7_REG, NP_XO_ABB_DIG);
+}
+
+static void hikey960_enable_ppll3(void)
+{
+ /* enable ppll3 */
+ mmio_write_32(PMC_PPLL3_CTRL0_REG, 0x4904305);
+ mmio_write_32(PMC_PPLL3_CTRL1_REG, 0x2300000);
+ mmio_write_32(PMC_PPLL3_CTRL1_REG, 0x6300000);
+}
+
+static void bus_idle_clear(unsigned int value)
+{
+ unsigned int pmc_value, value1, value2;
+ int timeout = 100;
+
+ pmc_value = value << 16;
+ pmc_value &= ~value;
+ mmio_write_32(PMC_NOC_POWER_IDLEREQ_REG, pmc_value);
+
+ for (;;) {
+ value1 = (unsigned int)mmio_read_32(PMC_NOC_POWER_IDLEACK_REG);
+ value2 = (unsigned int)mmio_read_32(PMC_NOC_POWER_IDLE_REG);
+ if (((value1 & value) == 0) && ((value2 & value) == 0))
+ break;
+ udelay(1);
+ timeout--;
+ if (timeout <= 0) {
+ WARN("%s timeout\n", __func__);
+ break;
+ }
+ }
+}
+
+static void set_vivobus_power_up(void)
+{
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV20_REG, 0x00020002);
+ mmio_write_32(CRG_PEREN0_REG, 0x00001000);
+}
+
+static void set_dss_power_up(void)
+{
+ /* set edc0 133MHz = 1600MHz / 12 */
+ mmio_write_32(CRG_CLKDIV5_REG, 0x003f000b);
+ /* set ldi0 ppl0 */
+ mmio_write_32(CRG_CLKDIV3_REG, 0xf0001000);
+ /* set ldi0 133MHz, 1600MHz / 12 */
+ mmio_write_32(CRG_CLKDIV5_REG, 0xfc002c00);
+ /* mtcmos on */
+ mmio_write_32(CRG_PERPWREN_REG, 0x00000020);
+ udelay(100);
+ /* DISP CRG */
+ mmio_write_32(CRG_PERRSTDIS4_REG, 0x00000010);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x01400140);
+ mmio_write_32(CRG_PEREN0_REG, 0x00002000);
+ mmio_write_32(CRG_PEREN3_REG, 0x0003b000);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(CRG_PERDIS3_REG, 0x0003b000);
+ mmio_write_32(CRG_PERDIS0_REG, 0x00002000);
+ mmio_write_32(CRG_CLKDIV18_REG, 0x01400000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(CRG_ISODIS_REG, 0x00000040);
+ /* unreset */
+ mmio_write_32(CRG_PERRSTDIS4_REG, 0x00000006);
+ mmio_write_32(CRG_PERRSTDIS3_REG, 0x00000c00);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x01400140);
+ mmio_write_32(CRG_PEREN0_REG, 0x00002000);
+ mmio_write_32(CRG_PEREN3_REG, 0x0003b000);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_DSS);
+ /* set edc0 400MHz for 2K 1600MHz / 4 */
+ mmio_write_32(CRG_CLKDIV5_REG, 0x003f0003);
+ /* set ldi 266MHz, 1600MHz / 6 */
+ mmio_write_32(CRG_CLKDIV5_REG, 0xfc001400);
+}
+
+static void set_vcodec_power_up(void)
+{
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV20_REG, 0x00040004);
+ mmio_write_32(CRG_PEREN0_REG, 0x00000060);
+ mmio_write_32(CRG_PEREN2_REG, 0x10000000);
+ /* unreset */
+ mmio_write_32(CRG_PERRSTDIS0_REG, 0x00000018);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_VCODEC);
+}
+
+static void set_vdec_power_up(void)
+{
+ /* mtcmos on */
+ mmio_write_32(CRG_PERPWREN_REG, 0x00000004);
+ udelay(100);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x80008000);
+ mmio_write_32(CRG_PEREN2_REG, 0x20080000);
+ mmio_write_32(CRG_PEREN3_REG, 0x00000800);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(CRG_PERDIS3_REG, 0x00000800);
+ mmio_write_32(CRG_PERDIS2_REG, 0x20080000);
+ mmio_write_32(CRG_CLKDIV18_REG, 0x80000000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(CRG_ISODIS_REG, 0x00000004);
+ /* unreset */
+ mmio_write_32(CRG_PERRSTDIS3_REG, 0x00000200);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x80008000);
+ mmio_write_32(CRG_PEREN2_REG, 0x20080000);
+ mmio_write_32(CRG_PEREN3_REG, 0x00000800);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_VDEC);
+}
+
+static void set_venc_power_up(void)
+{
+ /* set venc ppll3 */
+ mmio_write_32(CRG_CLKDIV8_REG, 0x18001000);
+ /* set venc 258MHz, 1290MHz / 5 */
+ mmio_write_32(CRG_CLKDIV8_REG, 0x07c00100);
+ /* mtcmos on */
+ mmio_write_32(CRG_PERPWREN_REG, 0x00000002);
+ udelay(100);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV19_REG, 0x00010001);
+ mmio_write_32(CRG_PEREN2_REG, 0x40000100);
+ mmio_write_32(CRG_PEREN3_REG, 0x00000400);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(CRG_PERDIS3_REG, 0x00000400);
+ mmio_write_32(CRG_PERDIS2_REG, 0x40000100);
+ mmio_write_32(CRG_CLKDIV19_REG, 0x00010000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(CRG_ISODIS_REG, 0x00000002);
+ /* unreset */
+ mmio_write_32(CRG_PERRSTDIS3_REG, 0x00000100);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV19_REG, 0x00010001);
+ mmio_write_32(CRG_PEREN2_REG, 0x40000100);
+ mmio_write_32(CRG_PEREN3_REG, 0x00000400);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_VENC);
+ /* set venc 645MHz, 1290MHz / 2 */
+ mmio_write_32(CRG_CLKDIV8_REG, 0x07c00040);
+}
+
+static void set_isp_power_up(void)
+{
+ /* mtcmos on */
+ mmio_write_32(CRG_PERPWREN_REG, 0x00000001);
+ udelay(100);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x70007000);
+ mmio_write_32(CRG_CLKDIV20_REG, 0x00100010);
+ mmio_write_32(CRG_PEREN5_REG, 0x01000010);
+ mmio_write_32(CRG_PEREN3_REG, 0x0bf00000);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(CRG_PERDIS5_REG, 0x01000010);
+ mmio_write_32(CRG_PERDIS3_REG, 0x0bf00000);
+ mmio_write_32(CRG_CLKDIV18_REG, 0x70000000);
+ mmio_write_32(CRG_CLKDIV20_REG, 0x00100000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(CRG_ISODIS_REG, 0x00000001);
+ /* unreset */
+ mmio_write_32(CRG_ISP_SEC_RSTDIS_REG, 0x0000002f);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV18_REG, 0x70007000);
+ mmio_write_32(CRG_CLKDIV20_REG, 0x00100010);
+ mmio_write_32(CRG_PEREN5_REG, 0x01000010);
+ mmio_write_32(CRG_PEREN3_REG, 0x0bf00000);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_ISP);
+ /* csi clk enable */
+ mmio_write_32(CRG_PEREN3_REG, 0x00700000);
+}
+
+static void set_ivp_power_up(void)
+{
+ /* set ivp ppll0 */
+ mmio_write_32(CRG_CLKDIV0_REG, 0xc0000000);
+ /* set ivp 267MHz, 1600MHz / 6 */
+ mmio_write_32(CRG_CLKDIV0_REG, 0x3c001400);
+ /* mtcmos on */
+ mmio_write_32(CRG_PERPWREN_REG, 0x00200000);
+ udelay(100);
+ /* IVP CRG unreset */
+ mmio_write_32(CRG_IVP_SEC_RSTDIS_REG, 0x00000001);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV20_REG, 0x02000200);
+ mmio_write_32(CRG_PEREN4_REG, 0x000000a8);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(CRG_PERDIS4_REG, 0x000000a8);
+ mmio_write_32(CRG_CLKDIV20_REG, 0x02000000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(CRG_ISODIS_REG, 0x01000000);
+ /* unreset */
+ mmio_write_32(CRG_IVP_SEC_RSTDIS_REG, 0x00000002);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV20_REG, 0x02000200);
+ mmio_write_32(CRG_PEREN4_REG, 0x000000a8);
+ /* bus idle clear */
+ bus_idle_clear(PMC_NOC_POWER_IDLEREQ_IVP);
+ /* set ivp 533MHz, 1600MHz / 3 */
+ mmio_write_32(CRG_CLKDIV0_REG, 0x3c000800);
+}
+
+static void set_audio_power_up(void)
+{
+ unsigned int ret;
+ int timeout = 100;
+ /* mtcmos on */
+ mmio_write_32(SCTRL_SCPWREN_REG, 0x00000001);
+ udelay(100);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV19_REG, 0x80108010);
+ mmio_write_32(SCTRL_SCCLKDIV2_REG, 0x00010001);
+ mmio_write_32(SCTRL_SCPEREN0_REG, 0x0c000000);
+ mmio_write_32(CRG_PEREN0_REG, 0x04000000);
+ mmio_write_32(CRG_PEREN5_REG, 0x00000080);
+ mmio_write_32(SCTRL_SCPEREN1_REG, 0x0000000f);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(SCTRL_SCPERDIS1_REG, 0x0000000f);
+ mmio_write_32(SCTRL_SCPERDIS0_REG, 0x0c000000);
+ mmio_write_32(CRG_PERDIS5_REG, 0x00000080);
+ mmio_write_32(CRG_PERDIS0_REG, 0x04000000);
+ mmio_write_32(SCTRL_SCCLKDIV2_REG, 0x00010000);
+ mmio_write_32(CRG_CLKDIV19_REG, 0x80100000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(SCTRL_SCISODIS_REG, 0x00000001);
+ udelay(1);
+ /* unreset */
+ mmio_write_32(SCTRL_PERRSTDIS1_SEC_REG, 0x00000001);
+ mmio_write_32(SCTRL_SCPERRSTDIS0_REG, 0x00000780);
+ /* clk enable */
+ mmio_write_32(CRG_CLKDIV19_REG, 0x80108010);
+ mmio_write_32(SCTRL_SCCLKDIV2_REG, 0x00010001);
+ mmio_write_32(SCTRL_SCPEREN0_REG, 0x0c000000);
+ mmio_write_32(CRG_PEREN0_REG, 0x04000000);
+ mmio_write_32(CRG_PEREN5_REG, 0x00000080);
+ mmio_write_32(SCTRL_SCPEREN1_REG, 0x0000000f);
+ /* bus idle clear */
+ mmio_write_32(SCTRL_SCPERCTRL7_REG, 0x00040000);
+ for (;;) {
+ ret = mmio_read_32(SCTRL_SCPERSTAT6_REG);
+ if (((ret & (1 << 5)) == 0) && ((ret & (1 << 8)) == 0))
+ break;
+ udelay(1);
+ timeout--;
+ if (timeout <= 0) {
+ WARN("%s timeout\n", __func__);
+ break;
+ }
+ }
+ mmio_write_32(ASP_CFG_MMBUF_CTRL_REG, 0x00ff0000);
+}
+
+static void set_pcie_power_up(void)
+{
+ /* mtcmos on */
+ mmio_write_32(SCTRL_SCPWREN_REG, 0x00000010);
+ udelay(100);
+ /* clk enable */
+ mmio_write_32(SCTRL_SCCLKDIV6_REG, 0x08000800);
+ mmio_write_32(SCTRL_SCPEREN2_REG, 0x00104000);
+ mmio_write_32(CRG_PEREN7_REG, 0x000003a0);
+ udelay(1);
+ /* clk disable */
+ mmio_write_32(SCTRL_SCPERDIS2_REG, 0x00104000);
+ mmio_write_32(CRG_PERDIS7_REG, 0x000003a0);
+ mmio_write_32(SCTRL_SCCLKDIV6_REG, 0x08000000);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(SCTRL_SCISODIS_REG, 0x00000030);
+ /* unreset */
+ mmio_write_32(CRG_PERRSTDIS3_REG, 0x8c000000);
+ /* clk enable */
+ mmio_write_32(SCTRL_SCCLKDIV6_REG, 0x08000800);
+ mmio_write_32(SCTRL_SCPEREN2_REG, 0x00104000);
+ mmio_write_32(CRG_PEREN7_REG, 0x000003a0);
+}
+
+static void ispfunc_enable(void)
+{
+ /* enable ispfunc. Otherwise powerup isp_srt causes exception. */
+ mmio_write_32(0xfff35000, 0x00000008);
+ mmio_write_32(0xfff35460, 0xc004ffff);
+ mmio_write_32(0xfff35030, 0x02000000);
+ mdelay(10);
+}
+
+static void isps_control_clock(int flag)
+{
+ unsigned int ret;
+
+ /* flag: 0 -- disable clock, 1 -- enable clock */
+ if (flag) {
+ ret = mmio_read_32(0xe8420364);
+ ret |= 1;
+ mmio_write_32(0xe8420364, ret);
+ } else {
+ ret = mmio_read_32(0xe8420364);
+ ret &= ~1;
+ mmio_write_32(0xe8420364, ret);
+ }
+}
+
+static void set_isp_srt_power_up(void)
+{
+ unsigned int ret;
+
+ ispfunc_enable();
+ /* reset */
+ mmio_write_32(0xe8420374, 0x00000001);
+ mmio_write_32(0xe8420350, 0x00000000);
+ mmio_write_32(0xe8420358, 0x00000000);
+ /* mtcmos on */
+ mmio_write_32(0xfff35150, 0x00400000);
+ udelay(100);
+ /* clk enable */
+ isps_control_clock(1);
+ udelay(1);
+ isps_control_clock(0);
+ udelay(1);
+ /* iso disable */
+ mmio_write_32(0xfff35148, 0x08000000);
+ /* unreset */
+ ret = mmio_read_32(0xe8420374);
+ ret &= ~0x1;
+ mmio_write_32(0xe8420374, ret);
+ /* clk enable */
+ isps_control_clock(1);
+ /* enable clock gating for accessing csi registers */
+ mmio_write_32(0xe8420010, ~0);
+}
+
+static void hikey960_regulator_enable(void)
+{
+ set_vivobus_power_up();
+ hikey960_enable_ppll3();
+ set_dss_power_up();
+ set_vcodec_power_up();
+ set_vdec_power_up();
+ set_venc_power_up();
+ set_isp_power_up();
+ set_ivp_power_up();
+ set_audio_power_up();
+ set_pcie_power_up();
+ set_isp_srt_power_up();
+}
+
+static void hikey960_ufs_reset(void)
+{
+ unsigned int data, mask;
+
+ mmio_write_32(CRG_PERDIS7_REG, 1 << 14);
+ mmio_clrbits_32(UFS_SYS_PHY_CLK_CTRL_REG, BIT_SYSCTRL_REF_CLOCK_EN);
+ do {
+ data = mmio_read_32(UFS_SYS_PHY_CLK_CTRL_REG);
+ } while (data & BIT_SYSCTRL_REF_CLOCK_EN);
+ /* use abb clk */
+ mmio_clrbits_32(UFS_SYS_UFS_SYSCTRL_REG, BIT_UFS_REFCLK_SRC_SE1);
+ mmio_clrbits_32(UFS_SYS_PHY_ISO_EN_REG, BIT_UFS_REFCLK_ISO_EN);
+ mmio_write_32(PCTRL_PERI_CTRL3_REG, (1 << 0) | (1 << 16));
+ mdelay(1);
+ mmio_write_32(CRG_PEREN7_REG, 1 << 14);
+ mmio_setbits_32(UFS_SYS_PHY_CLK_CTRL_REG, BIT_SYSCTRL_REF_CLOCK_EN);
+
+ mmio_write_32(CRG_PERRSTEN3_REG, PERI_UFS_BIT);
+ do {
+ data = mmio_read_32(CRG_PERRSTSTAT3_REG);
+ } while ((data & PERI_UFS_BIT) == 0);
+ mmio_setbits_32(UFS_SYS_PSW_POWER_CTRL_REG, BIT_UFS_PSW_MTCMOS_EN);
+ mdelay(1);
+ mmio_setbits_32(UFS_SYS_HC_LP_CTRL_REG, BIT_SYSCTRL_PWR_READY);
+ mmio_write_32(UFS_SYS_UFS_DEVICE_RESET_CTRL_REG,
+ MASK_UFS_DEVICE_RESET);
+ /* clear SC_DIV_UFS_PERIBUS */
+ mask = SC_DIV_UFS_PERIBUS << 16;
+ mmio_write_32(CRG_CLKDIV17_REG, mask);
+ /* set SC_DIV_UFSPHY_CFG(3) */
+ mask = SC_DIV_UFSPHY_CFG_MASK << 16;
+ data = SC_DIV_UFSPHY_CFG(3);
+ mmio_write_32(CRG_CLKDIV16_REG, mask | data);
+ data = mmio_read_32(UFS_SYS_PHY_CLK_CTRL_REG);
+ data &= ~MASK_SYSCTRL_CFG_CLOCK_FREQ;
+ data |= 0x39;
+ mmio_write_32(UFS_SYS_PHY_CLK_CTRL_REG, data);
+ mmio_clrbits_32(UFS_SYS_PHY_CLK_CTRL_REG, MASK_SYSCTRL_REF_CLOCK_SEL);
+ mmio_setbits_32(UFS_SYS_CLOCK_GATE_BYPASS_REG,
+ MASK_UFS_CLK_GATE_BYPASS);
+ mmio_setbits_32(UFS_SYS_UFS_SYSCTRL_REG, MASK_UFS_SYSCTRL_BYPASS);
+
+ mmio_setbits_32(UFS_SYS_PSW_CLK_CTRL_REG, BIT_SYSCTRL_PSW_CLK_EN);
+ mmio_clrbits_32(UFS_SYS_PSW_POWER_CTRL_REG, BIT_UFS_PSW_ISO_CTRL);
+ mmio_clrbits_32(UFS_SYS_PHY_ISO_EN_REG, BIT_UFS_PHY_ISO_CTRL);
+ mmio_clrbits_32(UFS_SYS_HC_LP_CTRL_REG, BIT_SYSCTRL_LP_ISOL_EN);
+ mmio_write_32(CRG_PERRSTDIS3_REG, PERI_ARST_UFS_BIT);
+ mmio_setbits_32(UFS_SYS_RESET_CTRL_EN_REG, BIT_SYSCTRL_LP_RESET_N);
+ mdelay(1);
+ mmio_write_32(UFS_SYS_UFS_DEVICE_RESET_CTRL_REG,
+ MASK_UFS_DEVICE_RESET | BIT_UFS_DEVICE_RESET);
+ mdelay(20);
+ mmio_write_32(UFS_SYS_UFS_DEVICE_RESET_CTRL_REG,
+ 0x03300330);
+
+ mmio_write_32(CRG_PERRSTDIS3_REG, PERI_UFS_BIT);
+ do {
+ data = mmio_read_32(CRG_PERRSTSTAT3_REG);
+ } while (data & PERI_UFS_BIT);
+}
+
+static void hikey960_ufs_init(void)
+{
+ dw_ufs_params_t ufs_params;
+
+ memset(&ufs_params, 0, sizeof(ufs_params));
+ ufs_params.reg_base = UFS_REG_BASE;
+ ufs_params.desc_base = HIKEY960_UFS_DESC_BASE;
+ ufs_params.desc_size = HIKEY960_UFS_DESC_SIZE;
+
+ if ((ufs_params.flags & UFS_FLAGS_SKIPINIT) == 0)
+ hikey960_ufs_reset();
+ dw_ufs_init(&ufs_params);
+}
+
+static void hikey960_tzc_init(void)
+{
+ mmio_write_32(TZC_EN0_REG, 0x7fbff066);
+ mmio_write_32(TZC_EN1_REG, 0xfffff5fc);
+ mmio_write_32(TZC_EN2_REG, 0x0007005c);
+ mmio_write_32(TZC_EN3_REG, 0x37030700);
+ mmio_write_32(TZC_EN4_REG, 0xf63fefae);
+ mmio_write_32(TZC_EN5_REG, 0x000410fd);
+ mmio_write_32(TZC_EN6_REG, 0x0063ff68);
+ mmio_write_32(TZC_EN7_REG, 0x030000f3);
+ mmio_write_32(TZC_EN8_REG, 0x00000007);
+}
+
+static void hikey960_peri_init(void)
+{
+ /* unreset */
+ mmio_setbits_32(CRG_PERRSTDIS4_REG, 1);
+}
+
+static void hikey960_pinmux_init(void)
+{
+ unsigned int id;
+
+ hikey960_read_boardid(&id);
+ if (id == 5301) {
+ /* hikey960 hardware v2 */
+ /* GPIO150: LED */
+ mmio_write_32(IOMG_FIX_006_REG, 0);
+ /* GPIO151: LED */
+ mmio_write_32(IOMG_FIX_007_REG, 0);
+ /* GPIO189: LED */
+ mmio_write_32(IOMG_AO_011_REG, 0);
+ /* GPIO190: LED */
+ mmio_write_32(IOMG_AO_012_REG, 0);
+ /* GPIO46 */
+ mmio_write_32(IOMG_044_REG, 0);
+ /* GPIO202 */
+ mmio_write_32(IOMG_AO_023_REG, 0);
+ /* GPIO206 */
+ mmio_write_32(IOMG_AO_026_REG, 0);
+ /* GPIO219 - PD pullup */
+ mmio_write_32(IOMG_AO_039_REG, 0);
+ mmio_write_32(IOCG_AO_043_REG, 1 << 0);
+ }
+ /* GPIO005 - PMU SSI, 10mA */
+ mmio_write_32(IOCG_006_REG, 2 << 4);
+}
+
+/*
+ * Function which will perform any remaining platform-specific setup that can
+ * occur after the MMU and data cache have been enabled.
+ */
+void bl1_platform_setup(void)
+{
+ hikey960_clk_init();
+ hikey960_pmu_init();
+ hikey960_regulator_enable();
+ hikey960_tzc_init();
+ hikey960_peri_init();
+ hikey960_ufs_init();
+ hikey960_pinmux_init();
+ hikey960_io_setup();
+}
+
+/*
+ * The following function checks if Firmware update is needed,
+ * by checking if TOC in FIP image is valid or not.
+ */
+unsigned int bl1_plat_get_next_image_id(void)
+{
+ unsigned int mode, ret;
+
+ mode = mmio_read_32(SCTRL_BAK_DATA0_REG);
+ switch (mode & BOOT_MODE_MASK) {
+ case BOOT_MODE_RECOVERY:
+ ret = NS_BL1U_IMAGE_ID;
+ break;
+ case BOOT_MODE_NORMAL:
+ ret = BL2_IMAGE_ID;
+ break;
+ default:
+ WARN("Invalid boot mode is found:%d\n", mode);
+ panic();
+ }
+ return ret;
+}
+
+image_desc_t *bl1_plat_get_image_desc(unsigned int image_id)
+{
+ unsigned int index = 0;
+
+ while (bl1_tbbr_image_descs[index].image_id != INVALID_IMAGE_ID) {
+ if (bl1_tbbr_image_descs[index].image_id == image_id)
+ return &bl1_tbbr_image_descs[index];
+ index++;
+ }
+
+ return NULL;
+}
+
+void bl1_plat_set_ep_info(unsigned int image_id,
+ entry_point_info_t *ep_info)
+{
+ unsigned int data = 0;
+ uintptr_t tmp = HIKEY960_NS_TMP_OFFSET;
+
+ if (image_id == BL2_IMAGE_ID)
+ return;
+ /* Copy NS BL1U from 0x1AC1_8000 to 0x1AC9_8000 */
+ memcpy((void *)tmp, (void *)HIKEY960_NS_IMAGE_OFFSET,
+ NS_BL1U_SIZE);
+ memcpy((void *)NS_BL1U_BASE, (void *)tmp, NS_BL1U_SIZE);
+ inv_dcache_range(NS_BL1U_BASE, NS_BL1U_SIZE);
+ /* Initialize the GIC driver, cpu and distributor interfaces */
+ gicv2_driver_init(&hikey960_gic_data);
+ gicv2_distif_init();
+ gicv2_pcpu_distif_init();
+ gicv2_cpuif_enable();
+ /* CNTFRQ is read-only in EL1 */
+ write_cntfrq_el0(plat_get_syscnt_freq2());
+ data = read_cpacr_el1();
+ do {
+ data |= 3 << 20;
+ write_cpacr_el1(data);
+ data = read_cpacr_el1();
+ } while ((data & (3 << 20)) != (3 << 20));
+ INFO("cpacr_el1:0x%x\n", data);
+
+ ep_info->args.arg0 = 0xffff & read_mpidr();
+ ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX,
+ DISABLE_ALL_EXCEPTIONS);
+}
diff --git a/plat/hisilicon/hikey960/hikey960_bl2_setup.c b/plat/hisilicon/hikey960/hikey960_bl2_setup.c
new file mode 100644
index 00000000..e2257937
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_bl2_setup.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <console.h>
+#include <debug.h>
+#include <errno.h>
+#include <generic_delay_timer.h>
+#include <hi3660.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <string.h>
+#include <ufs.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 BL2_RO_BASE (unsigned long)(&__RO_START__)
+#define BL2_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 BL2_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__)
+#define BL2_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__)
+
+static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE);
+
+typedef struct bl2_to_bl31_params_mem {
+ bl31_params_t bl31_params;
+ image_info_t bl31_image_info;
+ image_info_t bl32_image_info;
+ image_info_t bl33_image_info;
+ entry_point_info_t bl33_ep_info;
+ entry_point_info_t bl32_ep_info;
+ entry_point_info_t bl31_ep_info;
+} bl2_to_bl31_params_mem_t;
+
+static bl2_to_bl31_params_mem_t bl31_params_mem;
+
+meminfo_t *bl2_plat_sec_mem_layout(void)
+{
+ return &bl2_tzram_layout;
+}
+
+bl31_params_t *bl2_plat_get_bl31_params(void)
+{
+ bl31_params_t *bl2_to_bl31_params = NULL;
+
+ /*
+ * Initialise the memory for all the arguments that needs to
+ * be passed to BL3-1
+ */
+ memset(&bl31_params_mem, 0, sizeof(bl2_to_bl31_params_mem_t));
+
+ /* Assign memory for TF related information */
+ bl2_to_bl31_params = &bl31_params_mem.bl31_params;
+ SET_PARAM_HEAD(bl2_to_bl31_params, PARAM_BL31, VERSION_1, 0);
+
+ /* Fill BL3-1 related information */
+ bl2_to_bl31_params->bl31_image_info = &bl31_params_mem.bl31_image_info;
+ SET_PARAM_HEAD(bl2_to_bl31_params->bl31_image_info, PARAM_IMAGE_BINARY,
+ VERSION_1, 0);
+
+ /* Fill BL3-2 related information if it exists */
+#if BL32_BASE
+ bl2_to_bl31_params->bl32_ep_info = &bl31_params_mem.bl32_ep_info;
+ SET_PARAM_HEAD(bl2_to_bl31_params->bl32_ep_info, PARAM_EP,
+ VERSION_1, 0);
+ bl2_to_bl31_params->bl32_image_info = &bl31_params_mem.bl32_image_info;
+ SET_PARAM_HEAD(bl2_to_bl31_params->bl32_image_info, PARAM_IMAGE_BINARY,
+ VERSION_1, 0);
+#endif
+
+ /* Fill BL3-3 related information */
+ bl2_to_bl31_params->bl33_ep_info = &bl31_params_mem.bl33_ep_info;
+ SET_PARAM_HEAD(bl2_to_bl31_params->bl33_ep_info,
+ PARAM_EP, VERSION_1, 0);
+
+ /* BL3-3 expects to receive the primary CPU MPID (through x0) */
+ bl2_to_bl31_params->bl33_ep_info->args.arg0 = 0xffff & read_mpidr();
+
+ bl2_to_bl31_params->bl33_image_info = &bl31_params_mem.bl33_image_info;
+ SET_PARAM_HEAD(bl2_to_bl31_params->bl33_image_info, PARAM_IMAGE_BINARY,
+ VERSION_1, 0);
+
+ return bl2_to_bl31_params;
+}
+
+/*******************************************************************************
+ * Populate the extents of memory available for loading SCP_BL2 (if used),
+ * i.e. anywhere in trusted RAM as long as it doesn't overwrite BL2.
+ ******************************************************************************/
+void bl2_plat_get_scp_bl2_meminfo(meminfo_t *scp_bl2_meminfo)
+{
+ ufs_params_t ufs_params;
+
+ memset(&ufs_params, 0, sizeof(ufs_params_t));
+ ufs_params.reg_base = UFS_REG_BASE;
+ ufs_params.desc_base = HIKEY960_UFS_DESC_BASE;
+ ufs_params.desc_size = HIKEY960_UFS_DESC_SIZE;
+ ufs_params.flags = UFS_FLAGS_SKIPINIT;
+ ufs_init(NULL, &ufs_params);
+
+ hikey960_io_setup();
+
+ *scp_bl2_meminfo = bl2_tzram_layout;
+}
+
+extern int load_lpm3(void);
+
+int bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info)
+{
+ int i;
+ int *buf;
+
+ assert(scp_bl2_image_info->image_size < SCP_MEM_SIZE);
+
+ INFO("BL2: Initiating SCP_BL2 transfer to SCP\n");
+
+ INFO("BL2: SCP_BL2: 0x%lx@0x%x\n",
+ scp_bl2_image_info->image_base,
+ scp_bl2_image_info->image_size);
+
+ buf = (int *)scp_bl2_image_info->image_base;
+
+ INFO("BL2: SCP_BL2 HEAD:\n");
+ for (i = 0; i < 64; i += 4)
+ INFO("BL2: SCP_BL2 0x%x 0x%x 0x%x 0x%x\n",
+ buf[i], buf[i+1], buf[i+2], buf[i+3]);
+
+ buf = (int *)(scp_bl2_image_info->image_base +
+ scp_bl2_image_info->image_size - 256);
+
+ INFO("BL2: SCP_BL2 TAIL:\n");
+ for (i = 0; i < 64; i += 4)
+ INFO("BL2: SCP_BL2 0x%x 0x%x 0x%x 0x%x\n",
+ buf[i], buf[i+1], buf[i+2], buf[i+3]);
+
+ memcpy((void *)SCP_MEM_BASE,
+ (void *)scp_bl2_image_info->image_base,
+ scp_bl2_image_info->image_size);
+
+ INFO("BL2: SCP_BL2 transferred to SCP\n");
+
+ load_lpm3();
+ (void)buf;
+
+ return 0;
+}
+
+struct entry_point_info *bl2_plat_get_bl31_ep_info(void)
+{
+ return &bl31_params_mem.bl31_ep_info;
+}
+
+void bl2_plat_set_bl31_ep_info(image_info_t *image,
+ entry_point_info_t *bl31_ep_info)
+{
+ SET_SECURITY_STATE(bl31_ep_info->h.attr, SECURE);
+ bl31_ep_info->spsr = SPSR_64(MODE_EL3, MODE_SP_ELX,
+ DISABLE_ALL_EXCEPTIONS);
+}
+
+void bl2_plat_set_bl33_ep_info(image_info_t *image,
+ entry_point_info_t *bl33_ep_info)
+{
+ unsigned long el_status;
+ unsigned int mode;
+
+ /* Figure out what mode we enter the non-secure world in */
+ el_status = read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL2_SHIFT;
+ el_status &= ID_AA64PFR0_ELX_MASK;
+
+ if (el_status)
+ mode = MODE_EL2;
+ else
+ mode = MODE_EL1;
+
+ /*
+ * TODO: Consider the possibility of specifying the SPSR in
+ * the FIP ToC and allowing the platform to have a say as
+ * well.
+ */
+ bl33_ep_info->spsr = SPSR_64(mode, MODE_SP_ELX,
+ DISABLE_ALL_EXCEPTIONS);
+ SET_SECURITY_STATE(bl33_ep_info->h.attr, NON_SECURE);
+}
+
+void bl2_plat_flush_bl31_params(void)
+{
+ flush_dcache_range((unsigned long)&bl31_params_mem,
+ sizeof(bl2_to_bl31_params_mem_t));
+}
+
+void bl2_plat_get_bl33_meminfo(meminfo_t *bl33_meminfo)
+{
+ bl33_meminfo->total_base = DDR_BASE;
+ bl33_meminfo->total_size = DDR_SIZE;
+ bl33_meminfo->free_base = DDR_BASE;
+ bl33_meminfo->free_size = DDR_SIZE;
+}
+
+void bl2_early_platform_setup(meminfo_t *mem_layout)
+{
+ 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);
+
+ /* Setup the BL2 memory layout */
+ bl2_tzram_layout = *mem_layout;
+}
+
+void bl2_plat_arch_setup(void)
+{
+ hikey960_init_mmu_el1(bl2_tzram_layout.total_base,
+ bl2_tzram_layout.total_size,
+ BL2_RO_BASE,
+ BL2_RO_LIMIT,
+ BL2_COHERENT_RAM_BASE,
+ BL2_COHERENT_RAM_LIMIT);
+}
+
+void bl2_platform_setup(void)
+{
+ /* disable WDT0 */
+ if (mmio_read_32(WDT0_REG_BASE + WDT_LOCK_OFFSET) == WDT_LOCKED) {
+ mmio_write_32(WDT0_REG_BASE + WDT_LOCK_OFFSET, WDT_UNLOCK);
+ mmio_write_32(WDT0_REG_BASE + WDT_CONTROL_OFFSET, 0);
+ mmio_write_32(WDT0_REG_BASE + WDT_LOCK_OFFSET, 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_boardid.c b/plat/hisilicon/hikey960/hikey960_boardid.c
new file mode 100644
index 00000000..ac3e0385
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_boardid.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <hi3660.h>
+#include <mmio.h>
+
+#include "hikey960_private.h"
+
+#define ADC_ADCIN0 0
+#define ADC_ADCIN1 1
+#define ADC_ADCIN2 2
+
+#define HKADC_DATA_GRADE0 0
+#define HKADC_DATA_GRADE1 100
+#define HKADC_DATA_GRADE2 300
+#define HKADC_DATA_GRADE3 500
+#define HKADC_DATA_GRADE4 700
+#define HKADC_DATA_GRADE5 900
+#define HKADC_DATA_GRADE6 1100
+#define HKADC_DATA_GRADE7 1300
+#define HKADC_DATA_GRADE8 1500
+#define HKADC_DATA_GRADE9 1700
+#define HKADC_DATA_GRADE10 1800
+
+#define BOARDID_VALUE0 0
+#define BOARDID_VALUE1 1
+#define BOARDID_VALUE2 2
+#define BOARDID_VALUE3 3
+#define BOARDID_VALUE4 4
+#define BOARDID_VALUE5 5
+#define BOARDID_VALUE6 6
+#define BOARDID_VALUE7 7
+#define BOARDID_VALUE8 8
+#define BOARDID_VALUE9 9
+#define BOARDID_UNKNOWN 0xF
+
+#define BOARDID3_BASE 5
+
+
+static void init_adc(void)
+{
+ /* reset hkadc */
+ mmio_write_32(CRG_PERRSTEN2_REG, PERRSTEN2_HKADCSSI);
+ /* wait a few clock cycles */
+ udelay(2);
+ mmio_write_32(CRG_PERRSTDIS2_REG, PERRSTEN2_HKADCSSI);
+ udelay(2);
+ /* enable hkadc clock */
+ mmio_write_32(CRG_PERDIS2_REG, PEREN2_HKADCSSI);
+ udelay(2);
+ mmio_write_32(CRG_PEREN2_REG, PEREN2_HKADCSSI);
+ udelay(2);
+}
+
+static int get_adc(unsigned int channel, unsigned int *value)
+{
+ unsigned int data, value1, value0;
+
+ if (channel > HKADC_CHANNEL_MAX) {
+ WARN("invalid channel:%d\n", channel);
+ return -EFAULT;
+ }
+ /* configure the read/write operation for external HKADC */
+ mmio_write_32(HKADC_WR01_DATA_REG, HKADC_WR01_VALUE | channel);
+ mmio_write_32(HKADC_WR23_DATA_REG, HKADC_WR23_VALUE);
+ mmio_write_32(HKADC_WR45_DATA_REG, HKADC_WR45_VALUE);
+ /* configure the number of accessing registers */
+ mmio_write_32(HKADC_WR_NUM_REG, HKADC_WR_NUM_VALUE);
+ /* configure delay of accessing registers */
+ mmio_write_32(HKADC_DELAY01_REG, HKADC_CHANNEL0_DELAY01_VALUE);
+ mmio_write_32(HKADC_DELAY23_REG, HKADC_DELAY23_VALUE);
+
+ /* start HKADC */
+ mmio_write_32(HKADC_DSP_START_REG, 1);
+ do {
+ data = mmio_read_32(HKADC_DSP_START_REG);
+ } while (data & 1);
+
+ /* convert AD result */
+ value1 = mmio_read_32(HKADC_DSP_RD2_DATA_REG) & 0xffff;
+ value0 = mmio_read_32(HKADC_DSP_RD3_DATA_REG) & 0xffff;
+
+ data = ((value1 << 4) & HKADC_VALUE_HIGH) |
+ ((value0 >> 4) & HKADC_VALUE_LOW);
+ *value = data;
+ return 0;
+}
+
+static int get_value(unsigned int channel, unsigned int *value)
+{
+ int ret;
+
+ ret = get_adc(channel, value);
+ if (ret)
+ return ret;
+
+ /* convert ADC value to micro-volt */
+ ret = ((*value & HKADC_VALID_VALUE) * HKADC_VREF_1V8) / HKADC_ACCURACY;
+ *value = ret;
+ return 0;
+}
+
+static int adcin_data_remap(unsigned int adcin_value)
+{
+ int ret;
+
+ if (adcin_value < HKADC_DATA_GRADE0)
+ ret = BOARDID_UNKNOWN;
+ else if (adcin_value < HKADC_DATA_GRADE1)
+ ret = BOARDID_VALUE0;
+ else if (adcin_value < HKADC_DATA_GRADE2)
+ ret = BOARDID_VALUE1;
+ else if (adcin_value < HKADC_DATA_GRADE3)
+ ret = BOARDID_VALUE2;
+ else if (adcin_value < HKADC_DATA_GRADE4)
+ ret = BOARDID_VALUE3;
+ else if (adcin_value < HKADC_DATA_GRADE5)
+ ret = BOARDID_VALUE4;
+ else if (adcin_value < HKADC_DATA_GRADE6)
+ ret = BOARDID_VALUE5;
+ else if (adcin_value < HKADC_DATA_GRADE7)
+ ret = BOARDID_VALUE6;
+ else if (adcin_value < HKADC_DATA_GRADE8)
+ ret = BOARDID_VALUE7;
+ else if (adcin_value < HKADC_DATA_GRADE9)
+ ret = BOARDID_VALUE8;
+ else if (adcin_value < HKADC_DATA_GRADE10)
+ ret = BOARDID_VALUE9;
+ else
+ ret = BOARDID_UNKNOWN;
+ return ret;
+}
+
+int hikey960_read_boardid(unsigned int *id)
+{
+ unsigned int adcin0, adcin1, adcin2;
+ unsigned int adcin0_remap, adcin1_remap, adcin2_remap;
+
+ assert(id != NULL);
+
+ init_adc();
+
+ /* read ADC channel0 data */
+ get_value(ADC_ADCIN0, &adcin0);
+ adcin0_remap = adcin_data_remap(adcin0);
+ INFO("[BDID]adcin0:%d adcin0_remap:%d\n", adcin0, adcin0_remap);
+ if (adcin0_remap == BOARDID_UNKNOWN)
+ return -EINVAL;
+ /* read ADC channel1 data */
+ get_value(ADC_ADCIN1, &adcin1);
+ adcin1_remap = adcin_data_remap(adcin1);
+ INFO("[BDID]adcin1:%d adcin1_remap:%d\n", adcin1, adcin1_remap);
+ if (adcin1_remap == BOARDID_UNKNOWN)
+ return -EINVAL;
+ /* read ADC channel2 data */
+ get_value(ADC_ADCIN2, &adcin2);
+ adcin2_remap = adcin_data_remap(adcin2);
+ INFO("[BDID]adcin2:%d adcin2_remap:%d\n", adcin2, adcin2_remap);
+ if (adcin2_remap == BOARDID_UNKNOWN)
+ return -EINVAL;
+ *id = BOARDID3_BASE * 1000 + (adcin2_remap * 100) +
+ (adcin1_remap * 10) + adcin0_remap;
+ INFO("[BDID]boardid: %d\n", *id);
+ return 0;
+}
diff --git a/plat/hisilicon/hikey960/hikey960_def.h b/plat/hisilicon/hikey960/hikey960_def.h
new file mode 100644
index 00000000..e713e2e0
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_def.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __HIKEY960_DEF_H__
+#define __HIKEY960_DEF_H__
+
+#include <common_def.h>
+#include <tbbr_img_def.h>
+
+#define DDR_BASE 0x0
+#define DDR_SIZE 0xC0000000
+
+#define DEVICE_BASE 0xE0000000
+#define DEVICE_SIZE 0x20000000
+
+/*
+ * PL011 related constants
+ */
+#define PL011_UART5_BASE 0xFDF05000
+#define PL011_UART6_BASE 0xFFF32000
+#define PL011_BAUDRATE 115200
+#define PL011_UART_CLK_IN_HZ 19200000
+
+#define UFS_BASE 0
+/* FIP partition */
+#define HIKEY960_FIP_BASE (UFS_BASE + 0x1400000)
+#define HIKEY960_FIP_MAX_SIZE (12 << 20)
+
+#define HIKEY960_UFS_DESC_BASE 0x20000000
+#define HIKEY960_UFS_DESC_SIZE 0x00200000 /* 2MB */
+#define HIKEY960_UFS_DATA_BASE 0x10000000
+#define HIKEY960_UFS_DATA_SIZE 0x0A000000 /* 160MB */
+
+#endif /* __HIKEY960_DEF_H__ */
diff --git a/plat/hisilicon/hikey960/hikey960_io_storage.c b/plat/hisilicon/hikey960/hikey960_io_storage.c
new file mode 100644
index 00000000..de54e886
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_io_storage.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <firmware_image_package.h>
+#include <io_block.h>
+#include <io_driver.h>
+#include <io_fip.h>
+#include <io_memmap.h>
+#include <io_storage.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <semihosting.h> /* For FOPEN_MODE_... */
+#include <string.h>
+#include <ufs.h>
+
+struct plat_io_policy {
+ uintptr_t *dev_handle;
+ uintptr_t image_spec;
+ int (*check)(const uintptr_t spec);
+};
+
+static const io_dev_connector_t *ufs_dev_con, *fip_dev_con;
+static uintptr_t ufs_dev_handle, fip_dev_handle;
+
+static int check_ufs(const uintptr_t spec);
+static int check_fip(const uintptr_t spec);
+size_t ufs_read_lun3_blks(int lba, uintptr_t buf, size_t size);
+size_t ufs_write_lun3_blks(int lba, const uintptr_t buf, size_t size);
+
+static const io_block_spec_t ufs_fip_spec = {
+ .offset = HIKEY960_FIP_BASE,
+ .length = HIKEY960_FIP_MAX_SIZE,
+};
+
+static const io_block_spec_t ufs_data_spec = {
+ .offset = 0,
+ .length = 256 << 20,
+};
+
+static const io_block_dev_spec_t ufs_dev_spec = {
+ /* It's used as temp buffer in block driver. */
+ .buffer = {
+ .offset = HIKEY960_UFS_DATA_BASE,
+ .length = HIKEY960_UFS_DATA_SIZE,
+ },
+ .ops = {
+ .read = ufs_read_lun3_blks,
+ .write = ufs_write_lun3_blks,
+ },
+ .block_size = UFS_BLOCK_SIZE,
+};
+
+static const io_uuid_spec_t bl2_uuid_spec = {
+ .uuid = UUID_TRUSTED_BOOT_FIRMWARE_BL2,
+};
+
+static const io_uuid_spec_t scp_bl2_uuid_spec = {
+ .uuid = UUID_SCP_FIRMWARE_SCP_BL2,
+};
+
+static const io_uuid_spec_t bl31_uuid_spec = {
+ .uuid = UUID_EL3_RUNTIME_FIRMWARE_BL31,
+};
+
+static const io_uuid_spec_t bl33_uuid_spec = {
+ .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33,
+};
+
+static const struct plat_io_policy policies[] = {
+ [FIP_IMAGE_ID] = {
+ &ufs_dev_handle,
+ (uintptr_t)&ufs_fip_spec,
+ check_ufs
+ },
+ [BL2_IMAGE_ID] = {
+ &fip_dev_handle,
+ (uintptr_t)&bl2_uuid_spec,
+ check_fip
+ },
+ [SCP_BL2_IMAGE_ID] = {
+ &fip_dev_handle,
+ (uintptr_t)&scp_bl2_uuid_spec,
+ check_fip
+ },
+ [BL31_IMAGE_ID] = {
+ &fip_dev_handle,
+ (uintptr_t)&bl31_uuid_spec,
+ check_fip
+ },
+ [BL33_IMAGE_ID] = {
+ &fip_dev_handle,
+ (uintptr_t)&bl33_uuid_spec,
+ check_fip
+ },
+ [BL2U_IMAGE_ID] = {
+ &ufs_dev_handle,
+ (uintptr_t)&ufs_data_spec,
+ check_ufs
+ }
+};
+
+static int check_ufs(const uintptr_t spec)
+{
+ int result;
+ uintptr_t local_handle;
+
+ result = io_dev_init(ufs_dev_handle, (uintptr_t)NULL);
+ if (result == 0) {
+ result = io_open(ufs_dev_handle, spec, &local_handle);
+ if (result == 0)
+ io_close(local_handle);
+ }
+ return result;
+}
+
+static int check_fip(const uintptr_t spec)
+{
+ int result;
+ uintptr_t local_image_handle;
+
+ /* See if a Firmware Image Package is available */
+ result = io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID);
+ if (result == 0) {
+ result = io_open(fip_dev_handle, spec, &local_image_handle);
+ if (result == 0) {
+ VERBOSE("Using FIP\n");
+ io_close(local_image_handle);
+ }
+ }
+ return result;
+}
+
+void hikey960_io_setup(void)
+{
+ int result;
+
+ result = register_io_dev_block(&ufs_dev_con);
+ assert(result == 0);
+
+ result = register_io_dev_fip(&fip_dev_con);
+ assert(result == 0);
+
+ result = io_dev_open(ufs_dev_con, (uintptr_t)&ufs_dev_spec,
+ &ufs_dev_handle);
+ assert(result == 0);
+
+ result = io_dev_open(fip_dev_con, (uintptr_t)NULL, &fip_dev_handle);
+ assert(result == 0);
+
+ /* Ignore improbable errors in release builds */
+ (void)result;
+}
+
+/* Return an IO device handle and specification which can be used to access
+ * an image. Use this to enforce platform load policy
+ */
+int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
+ uintptr_t *image_spec)
+{
+ int result;
+ const struct plat_io_policy *policy;
+
+ assert(image_id < ARRAY_SIZE(policies));
+
+ policy = &policies[image_id];
+ result = policy->check(policy->image_spec);
+ assert(result == 0);
+
+ *image_spec = policy->image_spec;
+ *dev_handle = *(policy->dev_handle);
+
+ return result;
+}
+
+size_t ufs_read_lun3_blks(int lba, uintptr_t buf, size_t size)
+{
+ return ufs_read_blocks(3, lba, buf, size);
+}
+
+size_t ufs_write_lun3_blks(int lba, const uintptr_t buf, size_t size)
+{
+ return ufs_write_blocks(3, lba, buf, size);
+}
diff --git a/plat/hisilicon/hikey960/hikey960_mcu_load.c b/plat/hisilicon/hikey960/hikey960_mcu_load.c
new file mode 100644
index 00000000..7bf9a3d0
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_mcu_load.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <hi3660.h>
+#include <mmio.h>
+#include <string.h>
+
+#define ADDR_CONVERT(addr) ((addr) < 0x40000 ? \
+ (addr) + 0xFFF30000 : \
+ (addr) + 0x40000000)
+
+static void fw_data_init(void)
+{
+ unsigned long data_head_addr;
+ unsigned int *data_addr;
+
+ data_head_addr = mmio_read_32((uintptr_t) HISI_DATA_HEAD_BASE) + 0x14;
+ data_addr = (unsigned int *) ADDR_CONVERT(data_head_addr);
+
+ memcpy((void *)HISI_DATA0_BASE,
+ (const void *)(unsigned long)ADDR_CONVERT(data_addr[0]),
+ HISI_DATA0_SIZE);
+ memcpy((void *)HISI_DATA1_BASE,
+ (const void *)(unsigned long)ADDR_CONVERT(data_addr[1]),
+ HISI_DATA1_SIZE);
+}
+
+int load_lpm3(void)
+{
+ INFO("start fw loading\n");
+
+ fw_data_init();
+
+ flush_dcache_range((uintptr_t)HISI_RESERVED_MEM_BASE,
+ HISI_RESERVED_MEM_SIZE);
+
+ sev();
+ sev();
+
+ INFO("fw load success\n");
+
+ return 0;
+}
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
new file mode 100644
index 00000000..8f2a842e
--- /dev/null
+++ b/plat/hisilicon/hikey960/hikey960_private.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __HIKEY960_PRIVATE_H__
+#define __HIKEY960_PRIVATE_H__
+
+#include <bl_common.h>
+
+/*
+ * Function and variable prototypes
+ */
+void hikey960_init_mmu_el1(unsigned long total_base,
+ unsigned long total_size,
+ unsigned long ro_start,
+ unsigned long ro_limit,
+ unsigned long coh_start,
+ unsigned long coh_limit);
+void hikey960_init_mmu_el3(unsigned long total_base,
+ unsigned long total_size,
+ unsigned long ro_start,
+ unsigned long ro_limit,
+ unsigned long coh_start,
+ 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/hi3660.h b/plat/hisilicon/hikey960/include/hi3660.h
new file mode 100644
index 00000000..83d1b363
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/hi3660.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef __HI3660_H__
+#define __HI3660_H__
+
+#include <hi3660_crg.h>
+#include <hi3660_hkadc.h>
+#include <hi3660_mem_map.h>
+
+#define ASP_CFG_REG_BASE 0xE804E000
+
+#define ASP_CFG_MMBUF_CTRL_REG (ASP_CFG_REG_BASE + 0x148)
+
+#define LP_RAM_BASE 0xFFF50000
+
+#define SCTRL_REG_BASE 0xFFF0A000
+
+#define SCTRL_CONTROL_REG (SCTRL_REG_BASE + 0x000)
+#define SCTRL_CONTROL_SYS_MODE(x) (((x) & 0xf) << 3)
+#define SCTRL_CONTROL_SYS_MODE_NORMAL ((1 << 2) << 3)
+#define SCTRL_CONTROL_SYS_MODE_SLOW ((1 << 1) << 3)
+#define SCTRL_CONTROL_SYS_MODE_MASK (0xf << 3)
+#define SCTRL_CONTROL_MODE_CTRL_NORMAL (1 << 2)
+#define SCTRL_CONTROL_MODE_CTRL_SLOW (1 << 1)
+#define SCTRL_CONTROL_MODE_CTRL_MASK 0x7
+
+#define SCTRL_SCSYSSTAT_REG (SCTRL_REG_BASE + 0x004)
+
+#define SCTRL_DEEPSLEEPED_REG (SCTRL_REG_BASE + 0x008)
+#define SCTRL_EFUSE_USB_MASK (1 << 30)
+#define SCTRL_EFUSE_USB_PLL (1 << 30)
+#define SCTRL_EFUSE_USB_ABB (0 << 30)
+#define SCTRL_EFUSE_UFS_MASK (3 << 6)
+#define SCTRL_EFUSE_UFS_PLL (1 << 6)
+#define SCTRL_EFUSE_UFS_ABB (0 << 6)
+
+#define SCTRL_SCISOEN_REG (SCTRL_REG_BASE + 0x040)
+#define SCTRL_SCISODIS_REG (SCTRL_REG_BASE + 0x044)
+#define SCISO_MMBUFISO (1 << 3)
+
+#define SCTRL_SCPWREN_REG (SCTRL_REG_BASE + 0x060)
+#define SCPWREN_MMBUFPWREN (1 << 3)
+
+#define SCTRL_PLL_CTRL0_REG (SCTRL_REG_BASE + 0x100)
+#define SCTRL_PLL0_POSTDIV2(x) (((x) & 0x7) << 23)
+#define SCTRL_PLL0_POSTDIV1(x) (((x) & 0x7) << 20)
+#define SCTRL_PLL0_FBDIV(x) (((x) & 0xfff) << 8)
+#define SCTRL_PLL0_REFDIV(x) (((x) & 0x3f) << 2)
+#define SCTRL_PLL0_EN (1 << 0)
+
+#define SCTRL_PLL_CTRL1_REG (SCTRL_REG_BASE + 0x104)
+#define SCTRL_PLL0_CLK_NO_GATE (1 << 26)
+#define SCTRL_PLL0_CFG_VLD (1 << 25)
+#define SCTRL_PLL0_FRACDIV(x) ((x) & 0xFFFFFF)
+
+#define SCTRL_PLL_STAT_REG (SCTRL_REG_BASE + 0x10C)
+#define SCTRL_PLL0_STAT (1 << 0)
+
+#define SCTRL_SCPEREN0_REG (SCTRL_REG_BASE + 0x160)
+#define SCTRL_SCPERDIS0_REG (SCTRL_REG_BASE + 0x164)
+#define SCTRL_SCPERSTAT0_REG (SCTRL_REG_BASE + 0x168)
+
+#define SCTRL_SCPEREN1_REG (SCTRL_REG_BASE + 0x170)
+#define SCTRL_SCPERDIS1_REG (SCTRL_REG_BASE + 0x174)
+#define SCTRL_SCPEREN1_REG (SCTRL_REG_BASE + 0x170)
+#define SCTRL_SCPERDIS1_REG (SCTRL_REG_BASE + 0x174)
+#define SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS (1 << 31)
+#define SCPEREN_GT_PCLK_MMBUFCFG (1 << 25)
+#define SCPEREN_GT_PCLK_MMBUF (1 << 23)
+#define SCPEREN_GT_ACLK_MMBUF (1 << 22)
+#define SCPEREN_GT_CLK_NOC_AOBUS2MMBUF (1 << 6)
+
+#define SCTRL_SCPEREN2_REG (SCTRL_REG_BASE + 0x190)
+#define SCTRL_SCPERDIS2_REG (SCTRL_REG_BASE + 0x194)
+#define SCTRL_SCPERSTAT2_REG (SCTRL_REG_BASE + 0x198)
+#define SCTRL_SCPERRSTEN0_REG (SCTRL_REG_BASE + 0x200)
+#define SCTRL_SCPERRSTDIS0_REG (SCTRL_REG_BASE + 0x204)
+#define SCTRL_SCPERRSTSTAT0_REG (SCTRL_REG_BASE + 0x208)
+#define SCTRL_SCPERRSTEN1_REG (SCTRL_REG_BASE + 0x20C)
+#define SCTRL_SCPERRSTDIS1_REG (SCTRL_REG_BASE + 0x210)
+#define SCTRL_SCPERRSTSTAT1_REG (SCTRL_REG_BASE + 0x214)
+#define IP_RST_MMBUFCFG (1 << 12)
+#define IP_RST_MMBUF (1 << 11)
+
+#define SCTRL_SCPERRSTEN2_REG (SCTRL_REG_BASE + 0x218)
+#define SCTRL_SCPERRSTDIS2_REG (SCTRL_REG_BASE + 0x21C)
+#define SCTRL_SCPERRSTSTAT2_REG (SCTRL_REG_BASE + 0x220)
+
+#define SCTRL_SCCLKDIV2_REG (SCTRL_REG_BASE + 0x258)
+#define SEL_CLK_MMBUF_MASK (0x3 << 8)
+#define SEL_CLK_MMBUF_PLL0 (0x3 << 8)
+#define SCCLKDIV2_GT_PCLK_MMBUF (1 << 7)
+
+#define SCTRL_SCCLKDIV4_REG (SCTRL_REG_BASE + 0x260)
+#define GT_MMBUF_SYS (1 << 13)
+#define GT_MMBUF_FLL (1 << 12)
+#define GT_PLL_CLK_MMBUF (1 << 11)
+
+#define SCTRL_SCCLKDIV6_REG (SCTRL_REG_BASE + 0x268)
+
+#define SCTRL_SCPERCTRL7_REG (SCTRL_REG_BASE + 0x31C)
+#define SCTRL_SCPERSTAT6_REG (SCTRL_REG_BASE + 0x378)
+
+#define SCTRL_SCINNERSTAT_REG (SCTRL_REG_BASE + 0x3A0)
+#define EMMC_UFS_SEL (1 << 15)
+
+#define SCTRL_BAK_DATA0_REG (SCTRL_REG_BASE + 0x40C)
+#define SCTRL_BAK_DATA4_REG (SCTRL_REG_BASE + 0x41C)
+
+#define SCTRL_LPMCU_CLKEN_REG (SCTRL_REG_BASE + 0x480)
+#define SCTRL_LPMCU_CLKDIS_REG (SCTRL_REG_BASE + 0x484)
+#define SCTRL_LPMCU_RSTEN_REG (SCTRL_REG_BASE + 0x500)
+#define SCTRL_LPMCU_RSTDIS_REG (SCTRL_REG_BASE + 0x504)
+#define DDRC_SOFT_BIT (1 << 6)
+#define DDRC_CLK_BIT (1 << 5)
+
+#define SCTRL_SCPEREN0_SEC_REG (SCTRL_REG_BASE + 0x900)
+#define SCTRL_SCPERDIS0_SEC_REG (SCTRL_REG_BASE + 0x904)
+#define MMBUF_SEC_CTRL_MASK (0xfff << 20)
+#define MMBUF_SEC_CTRL(x) (((x) & 0xfff) << 20)
+
+#define SCTRL_PERRSTEN1_SEC_REG (SCTRL_REG_BASE + 0xA50)
+#define SCTRL_PERRSTDIS1_SEC_REG (SCTRL_REG_BASE + 0xA54)
+#define SCTRL_PERRSTSTAT1_SEC_REG (SCTRL_REG_BASE + 0xA58)
+#define RST_ASP_SUBSYS_BIT (1 << 0)
+
+#define SCTRL_PERRSTEN2_SEC_REG (SCTRL_REG_BASE + 0xB50)
+#define SCTRL_PERRSTDIS2_SEC_REG (SCTRL_REG_BASE + 0xB54)
+#define SCTRL_PERRSTSTAT2_SEC_REG (SCTRL_REG_BASE + 0xB58)
+
+#define SCTRL_HISEECLKDIV_REG (SCTRL_REG_BASE + 0xC28)
+#define SC_SEL_HISEE_PLL_MASK (1 << 4)
+#define SC_SEL_HISEE_PLL0 (1 << 4)
+#define SC_SEL_HISEE_PLL2 (0 << 4)
+#define SC_DIV_HISEE_PLL_MASK (7 << 16)
+#define SC_DIV_HISEE_PLL(x) ((x) & 0x7)
+
+#define SCTRL_SCSOCID0_REG (SCTRL_REG_BASE + 0xE00)
+
+#define PMC_REG_BASE 0xFFF31000
+#define PMC_PPLL1_CTRL0_REG (PMC_REG_BASE + 0x038)
+#define PMC_PPLL1_CTRL1_REG (PMC_REG_BASE + 0x03C)
+#define PMC_PPLL2_CTRL0_REG (PMC_REG_BASE + 0x040)
+#define PMC_PPLL2_CTRL1_REG (PMC_REG_BASE + 0x044)
+#define PMC_PPLL3_CTRL0_REG (PMC_REG_BASE + 0x048)
+#define PMC_PPLL3_CTRL1_REG (PMC_REG_BASE + 0x04C)
+#define PPLLx_LOCK (1 << 26)
+#define PPLLx_WITHOUT_CLK_GATE (1 << 26)
+#define PPLLx_CFG_VLD (1 << 25)
+#define PPLLx_INT_MOD (1 << 24)
+#define PPLLx_POSTDIV2_MASK (0x7 << 23)
+#define PPLLx_POSTDIV2(x) (((x) & 0x7) << 23)
+#define PPLLx_POSTDIV1_MASK (0x7 << 20)
+#define PPLLx_POSTDIV1(x) (((x) & 0x7) << 20)
+#define PPLLx_FRACDIV_MASK (0x00FFFFFF)
+#define PPLLx_FRACDIV(x) ((x) & 0x00FFFFFF)
+#define PPLLx_FBDIV_MASK (0xfff << 8)
+#define PPLLx_FBDIV(x) (((x) & 0xfff) << 8)
+#define PPLLx_REFDIV_MASK (0x3f << 2)
+#define PPLLx_REFDIV(x) (((x) & 0x3f) << 2)
+#define PPLLx_BP (1 << 1)
+#define PPLLx_EN (1 << 0)
+
+#define PMC_DDRLP_CTRL_REG (PMC_REG_BASE + 0x30C)
+#define DDRC_CSYSREQ_CFG(x) ((x) & 0xF)
+
+#define PMC_NOC_POWER_IDLEREQ_REG (PMC_REG_BASE + 0x380)
+#define PMC_NOC_POWER_IDLEREQ_IVP (1 << 14)
+#define PMC_NOC_POWER_IDLEREQ_DSS (1 << 13)
+#define PMC_NOC_POWER_IDLEREQ_VENC (1 << 11)
+#define PMC_NOC_POWER_IDLEREQ_VDEC (1 << 10)
+#define PMC_NOC_POWER_IDLEREQ_ISP (1 << 5)
+#define PMC_NOC_POWER_IDLEREQ_VCODEC (1 << 4)
+#define DDRPHY_BYPASS_MODE (1 << 0)
+
+#define PMC_NOC_POWER_IDLEACK_REG (PMC_REG_BASE + 0x384)
+#define PMC_NOC_POWER_IDLE_REG (PMC_REG_BASE + 0x388)
+
+#define PMU_SSI0_REG_BASE 0xFFF34000
+
+#define PMU_SSI0_LDO8_CTRL0_REG (PMU_SSI0_REG_BASE + (0x68 << 2))
+#define LDO8_CTRL0_EN_1_8V 0x02
+
+#define PMU_SSI0_CLK_TOP_CTRL7_REG (PMU_SSI0_REG_BASE + (0x10C << 2))
+#define NP_XO_ABB_DIG (1 << 1)
+
+#define LP_CONFIG_REG_BASE 0xFFF3F000
+
+#define DMAC_BASE 0xFDF30000
+
+#define CCI400_REG_BASE 0xE8100000
+#define CCI400_SL_IFACE3_CLUSTER_IX 0
+#define CCI400_SL_IFACE4_CLUSTER_IX 1
+
+#define GICD_REG_BASE 0xE82B1000
+#define GICC_REG_BASE 0xE82B2000
+/*
+ * GIC400 interrupt handling related constants
+ */
+#define IRQ_SEC_PHY_TIMER 29
+#define IRQ_SEC_SGI_0 8
+#define IRQ_SEC_SGI_1 9
+#define IRQ_SEC_SGI_2 10
+#define IRQ_SEC_SGI_3 11
+#define IRQ_SEC_SGI_4 12
+#define IRQ_SEC_SGI_5 13
+#define IRQ_SEC_SGI_6 14
+#define IRQ_SEC_SGI_7 15
+#define IRQ_SEC_SGI_8 16
+
+#define IPC_REG_BASE 0xE896A000
+#define IPC_BASE (IPC_REG_BASE)
+
+#define IOMG_REG_BASE 0xE896C000
+
+/* GPIO46: HUB 3.3V enable. active low */
+#define IOMG_044_REG (IOMG_REG_BASE + 0x0B0)
+#define IOMG_UART5_RX_REG (IOMG_REG_BASE + 0x0BC)
+#define IOMG_UART5_TX_REG (IOMG_REG_BASE + 0x0C0)
+
+#define IOCG_REG_BASE 0xE896C800
+
+/* GPIO005: PMIC SSI. (2 << 4) */
+#define IOCG_006_REG (IOCG_REG_BASE + 0x018)
+
+#define TIMER9_REG_BASE 0xE8A00000
+
+#define WDT0_REG_BASE 0xE8A06000
+#define WDT1_REG_BASE 0xE8A07000
+#define WDT_CONTROL_OFFSET 0x008
+#define WDT_LOCK_OFFSET 0xC00
+
+#define WDT_UNLOCK 0x1ACCE551
+#define WDT_LOCKED 1
+
+#define PCTRL_REG_BASE 0xE8A09000
+#define PCTRL_PERI_CTRL3_REG (PCTRL_REG_BASE + 0x010)
+#define PCTRL_PERI_CTRL24_REG (PCTRL_REG_BASE + 0x064)
+
+#define TZC_REG_BASE 0xE8A21000
+#define TZC_STAT0_REG (TZC_REG_BASE + 0x800)
+#define TZC_EN0_REG (TZC_REG_BASE + 0x804)
+#define TZC_DIS0_REG (TZC_REG_BASE + 0x808)
+#define TZC_STAT1_REG (TZC_REG_BASE + 0x80C)
+#define TZC_EN1_REG (TZC_REG_BASE + 0x810)
+#define TZC_DIS1_REG (TZC_REG_BASE + 0x814)
+#define TZC_STAT2_REG (TZC_REG_BASE + 0x818)
+#define TZC_EN2_REG (TZC_REG_BASE + 0x81C)
+#define TZC_DIS2_REG (TZC_REG_BASE + 0x820)
+#define TZC_STAT3_REG (TZC_REG_BASE + 0x824)
+#define TZC_EN3_REG (TZC_REG_BASE + 0x828)
+#define TZC_DIS3_REG (TZC_REG_BASE + 0x82C)
+#define TZC_STAT4_REG (TZC_REG_BASE + 0x830)
+#define TZC_EN4_REG (TZC_REG_BASE + 0x834)
+#define TZC_DIS4_REG (TZC_REG_BASE + 0x838)
+#define TZC_STAT5_REG (TZC_REG_BASE + 0x83C)
+#define TZC_EN5_REG (TZC_REG_BASE + 0x840)
+#define TZC_DIS5_REG (TZC_REG_BASE + 0x844)
+#define TZC_STAT6_REG (TZC_REG_BASE + 0x848)
+#define TZC_EN6_REG (TZC_REG_BASE + 0x84C)
+#define TZC_DIS6_REG (TZC_REG_BASE + 0x850)
+#define TZC_STAT7_REG (TZC_REG_BASE + 0x854)
+#define TZC_EN7_REG (TZC_REG_BASE + 0x858)
+#define TZC_DIS7_REG (TZC_REG_BASE + 0x85C)
+#define TZC_STAT8_REG (TZC_REG_BASE + 0x860)
+#define TZC_EN8_REG (TZC_REG_BASE + 0x864)
+#define TZC_DIS8_REG (TZC_REG_BASE + 0x868)
+
+#define MMBUF_BASE 0xEA800000
+
+#define ACPU_DMCPACK0_BASE 0xEA900000
+
+#define ACPU_DMCPACK1_BASE 0xEA920000
+
+#define ACPU_DMCPACK2_BASE 0xEA940000
+
+#define ACPU_DMCPACK3_BASE 0xEA960000
+
+#define UART5_REG_BASE 0xFDF05000
+
+#define USB3OTG_REG_BASE 0xFF100000
+
+#define UFS_REG_BASE 0xFF3B0000
+
+#define UFS_SYS_REG_BASE 0xFF3B1000
+
+#define UFS_SYS_PSW_POWER_CTRL_REG (UFS_SYS_REG_BASE + 0x004)
+#define UFS_SYS_PHY_ISO_EN_REG (UFS_SYS_REG_BASE + 0x008)
+#define UFS_SYS_HC_LP_CTRL_REG (UFS_SYS_REG_BASE + 0x00C)
+#define UFS_SYS_PHY_CLK_CTRL_REG (UFS_SYS_REG_BASE + 0x010)
+#define UFS_SYS_PSW_CLK_CTRL_REG (UFS_SYS_REG_BASE + 0x014)
+#define UFS_SYS_CLOCK_GATE_BYPASS_REG (UFS_SYS_REG_BASE + 0x018)
+#define UFS_SYS_RESET_CTRL_EN_REG (UFS_SYS_REG_BASE + 0x01C)
+#define UFS_SYS_MONITOR_HH_REG (UFS_SYS_REG_BASE + 0x03C)
+#define UFS_SYS_UFS_SYSCTRL_REG (UFS_SYS_REG_BASE + 0x05C)
+#define UFS_SYS_UFS_DEVICE_RESET_CTRL_REG (UFS_SYS_REG_BASE + 0x060)
+#define UFS_SYS_UFS_APB_ADDR_MASK_REG (UFS_SYS_REG_BASE + 0x064)
+
+#define BIT_UFS_PSW_ISO_CTRL (1 << 16)
+#define BIT_UFS_PSW_MTCMOS_EN (1 << 0)
+#define BIT_UFS_REFCLK_ISO_EN (1 << 16)
+#define BIT_UFS_PHY_ISO_CTRL (1 << 0)
+#define BIT_SYSCTRL_LP_ISOL_EN (1 << 16)
+#define BIT_SYSCTRL_PWR_READY (1 << 8)
+#define BIT_SYSCTRL_REF_CLOCK_EN (1 << 24)
+#define MASK_SYSCTRL_REF_CLOCK_SEL (3 << 8)
+#define MASK_SYSCTRL_CFG_CLOCK_FREQ (0xFF)
+#define BIT_SYSCTRL_PSW_CLK_EN (1 << 4)
+#define MASK_UFS_CLK_GATE_BYPASS (0x3F)
+#define BIT_SYSCTRL_LP_RESET_N (1 << 0)
+#define BIT_UFS_REFCLK_SRC_SE1 (1 << 0)
+#define MASK_UFS_SYSCTRL_BYPASS (0x3F << 16)
+#define MASK_UFS_DEVICE_RESET (1 << 16)
+#define BIT_UFS_DEVICE_RESET (1 << 0)
+
+#define IOMG_FIX_REG_BASE 0xFF3B6000
+
+/* GPIO150: LED */
+#define IOMG_FIX_006_REG (IOMG_FIX_REG_BASE + 0x018)
+/* GPIO151: LED */
+#define IOMG_FIX_007_REG (IOMG_FIX_REG_BASE + 0x01C)
+
+#define IOMG_AO_REG_BASE 0xFFF11000
+
+/* GPIO189: LED */
+#define IOMG_AO_011_REG (IOMG_AO_REG_BASE + 0x02C)
+/* GPIO190: LED */
+#define IOMG_AO_012_REG (IOMG_AO_REG_BASE + 0x030)
+/* GPIO202: type C enable. active low */
+#define IOMG_AO_023_REG (IOMG_AO_REG_BASE + 0x05C)
+/* GPIO206: USB switch. active low */
+#define IOMG_AO_026_REG (IOMG_AO_REG_BASE + 0x068)
+/* GPIO219: PD interrupt. pull up */
+#define IOMG_AO_039_REG (IOMG_AO_REG_BASE + 0x09C)
+
+#define IOCG_AO_REG_BASE 0xFFF1187C
+/* GPIO219: PD interrupt. pull up */
+#define IOCG_AO_043_REG (IOCG_AO_REG_BASE + 0x030)
+
+#endif /* __HI3660_H__ */
diff --git a/plat/hisilicon/hikey960/include/hi3660_crg.h b/plat/hisilicon/hikey960/include/hi3660_crg.h
new file mode 100644
index 00000000..db1df9ed
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/hi3660_crg.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef __HI3660_CRG_H__
+#define __HI3660_CRG_H__
+
+#define CRG_REG_BASE 0xFFF35000
+
+#define CRG_PEREN0_REG (CRG_REG_BASE + 0x000)
+#define CRG_PERDIS0_REG (CRG_REG_BASE + 0x004)
+#define CRG_PERSTAT0_REG (CRG_REG_BASE + 0x008)
+#define PEREN0_GT_CLK_AOMM (1 << 31)
+
+#define CRG_PEREN1_REG (CRG_REG_BASE + 0x010)
+#define CRG_PERDIS1_REG (CRG_REG_BASE + 0x014)
+#define CRG_PERSTAT1_REG (CRG_REG_BASE + 0x018)
+#define CRG_PEREN2_REG (CRG_REG_BASE + 0x020)
+#define CRG_PERDIS2_REG (CRG_REG_BASE + 0x024)
+#define CRG_PERSTAT2_REG (CRG_REG_BASE + 0x028)
+#define PEREN2_HKADCSSI (1 << 24)
+
+#define CRG_PEREN3_REG (CRG_REG_BASE + 0x030)
+#define CRG_PERDIS3_REG (CRG_REG_BASE + 0x034)
+
+#define CRG_PEREN4_REG (CRG_REG_BASE + 0x040)
+#define CRG_PERDIS4_REG (CRG_REG_BASE + 0x044)
+#define CRG_PERCLKEN4_REG (CRG_REG_BASE + 0x048)
+#define CRG_PERSTAT4_REG (CRG_REG_BASE + 0x04C)
+#define GT_ACLK_USB3OTG (1 << 1)
+#define GT_CLK_USB3OTG_REF (1 << 0)
+
+#define CRG_PEREN5_REG (CRG_REG_BASE + 0x050)
+#define CRG_PERDIS5_REG (CRG_REG_BASE + 0x054)
+#define CRG_PERSTAT5_REG (CRG_REG_BASE + 0x058)
+#define CRG_PERRSTEN0_REG (CRG_REG_BASE + 0x060)
+#define CRG_PERRSTDIS0_REG (CRG_REG_BASE + 0x064)
+#define CRG_PERRSTSTAT0_REG (CRG_REG_BASE + 0x068)
+#define CRG_PERRSTEN1_REG (CRG_REG_BASE + 0x06C)
+#define CRG_PERRSTDIS1_REG (CRG_REG_BASE + 0x070)
+#define CRG_PERRSTSTAT1_REG (CRG_REG_BASE + 0x074)
+#define CRG_PERRSTEN2_REG (CRG_REG_BASE + 0x078)
+#define CRG_PERRSTDIS2_REG (CRG_REG_BASE + 0x07C)
+#define CRG_PERRSTSTAT2_REG (CRG_REG_BASE + 0x080)
+#define PERRSTEN2_HKADCSSI (1 << 24)
+
+#define CRG_PERRSTEN3_REG (CRG_REG_BASE + 0x084)
+#define CRG_PERRSTDIS3_REG (CRG_REG_BASE + 0x088)
+#define CRG_PERRSTSTAT3_REG (CRG_REG_BASE + 0x08C)
+#define CRG_PERRSTEN4_REG (CRG_REG_BASE + 0x090)
+#define CRG_PERRSTDIS4_REG (CRG_REG_BASE + 0x094)
+#define CRG_PERRSTSTAT4_REG (CRG_REG_BASE + 0x098)
+#define IP_RST_USB3OTG_MUX (1 << 8)
+#define IP_RST_USB3OTG_AHBIF (1 << 7)
+#define IP_RST_USB3OTG_32K (1 << 6)
+#define IP_RST_USB3OTG (1 << 5)
+#define IP_RST_USB3OTGPHY_POR (1 << 3)
+
+#define CRG_PERRSTEN5_REG (CRG_REG_BASE + 0x09C)
+#define CRG_PERRSTDIS5_REG (CRG_REG_BASE + 0x0A0)
+#define CRG_PERRSTSTAT5_REG (CRG_REG_BASE + 0x0A4)
+
+/* bit fields in CRG_PERI */
+#define PERI_PCLK_PCTRL_BIT (1 << 31)
+#define PERI_TIMER12_BIT (1 << 25)
+#define PERI_TIMER11_BIT (1 << 24)
+#define PERI_TIMER10_BIT (1 << 23)
+#define PERI_TIMER9_BIT (1 << 22)
+#define PERI_UART5_BIT (1 << 15)
+#define PERI_UFS_BIT (1 << 12)
+#define PERI_ARST_UFS_BIT (1 << 7)
+#define PERI_PPLL2_EN_CPU (1 << 3)
+#define PERI_PWM_BIT (1 << 0)
+#define PERI_DDRC_BIT (1 << 0)
+#define PERI_DDRC_D_BIT (1 << 4)
+#define PERI_DDRC_C_BIT (1 << 3)
+#define PERI_DDRC_B_BIT (1 << 2)
+#define PERI_DDRC_A_BIT (1 << 1)
+#define PERI_DDRC_DMUX_BIT (1 << 0)
+
+#define CRG_CLKDIV0_REG (CRG_REG_BASE + 0x0A0)
+#define SC_DIV_LPMCU_MASK ((0x1F << 5) << 16)
+#define SC_DIV_LPMCU(x) (((x) & 0x1F) << 5)
+
+#define CRG_CLKDIV1_REG (CRG_REG_BASE + 0x0B0)
+#define SEL_LPMCU_PLL_MASK ((1 << 1) << 16)
+#define SEL_SYSBUS_MASK ((1 << 0) << 16)
+#define SEL_LPMCU_PLL1 (1 << 1)
+#define SEL_LPMCU_PLL0 (0 << 1)
+#define SEL_SYSBUS_PLL0 (1 << 0)
+#define SEL_SYSBUS_PLL1 (0 << 0)
+
+#define CRG_CLKDIV3_REG (CRG_REG_BASE + 0x0B4)
+#define CRG_CLKDIV5_REG (CRG_REG_BASE + 0x0BC)
+#define CRG_CLKDIV8_REG (CRG_REG_BASE + 0x0C8)
+
+#define CRG_CLKDIV12_REG (CRG_REG_BASE + 0x0D8)
+#define SC_DIV_A53HPM_MASK (0x7 << 13)
+#define SC_DIV_A53HPM(x) (((x) & 0x7) << 13)
+
+#define CRG_CLKDIV16_REG (CRG_REG_BASE + 0x0E8)
+#define DDRC_CLK_SW_REQ_CFG_MASK (0x3 << 12)
+#define DDRC_CLK_SW_REQ_CFG(x) (((x) & 0x3) << 12)
+#define SC_DIV_UFSPHY_CFG_MASK (0x3 << 9)
+#define SC_DIV_UFSPHY_CFG(x) (((x) & 0x3) << 9)
+#define DDRCPLL_SW (1 << 8)
+
+#define CRG_CLKDIV17_REG (CRG_REG_BASE + 0x0EC)
+#define SC_DIV_UFS_PERIBUS (1 << 14)
+
+#define CRG_CLKDIV18_REG (CRG_REG_BASE + 0x0F0)
+#define CRG_CLKDIV19_REG (CRG_REG_BASE + 0x0F4)
+#define CRG_CLKDIV20_REG (CRG_REG_BASE + 0x0F8)
+#define CLKDIV20_GT_CLK_AOMM (1 << 3)
+
+#define CRG_CLKDIV22_REG (CRG_REG_BASE + 0x100)
+#define SEL_PLL_320M_MASK (1 << 16)
+#define SEL_PLL2_320M (1 << 0)
+#define SEL_PLL0_320M (0 << 0)
+
+#define CRG_CLKDIV23_REG (CRG_REG_BASE + 0x104)
+#define PERI_DDRC_SW_BIT (1 << 13)
+#define DIV_CLK_DDRSYS_MASK (0x3 << 10)
+#define DIV_CLK_DDRSYS(x) (((x) & 0x3) << 10)
+#define GET_DIV_CLK_DDRSYS(x) (((x) & DIV_CLK_DDRSYS_MASK) >> 10)
+#define DIV_CLK_DDRCFG_MASK (0x6 << 5)
+#define DIV_CLK_DDRCFG(x) (((x) & 0x6) << 5)
+#define GET_DIV_CLK_DDRCFG(x) (((x) & DIV_CLK_DDRCFG_MASK) >> 5)
+#define DIV_CLK_DDRC_MASK 0x1F
+#define DIV_CLK_DDRC(x) ((x) & DIV_CLK_DDRC_MASK)
+#define GET_DIV_CLK_DDRC(x) ((x) & DIV_CLK_DDRC_MASK)
+
+#define CRG_CLKDIV25_REG (CRG_REG_BASE + 0x10C)
+#define DIV_SYSBUS_PLL_MASK (0xF << 16)
+#define DIV_SYSBUS_PLL(x) ((x) & 0xF)
+
+#define CRG_PERI_CTRL2_REG (CRG_REG_BASE + 0x128)
+#define PERI_TIME_STAMP_CLK_MASK (0x7 << 28)
+#define PERI_TIME_STAMP_CLK_DIV(x) (((x) & 0x7) << 22)
+
+#define CRG_ISODIS_REG (CRG_REG_BASE + 0x148)
+#define CRG_PERPWREN_REG (CRG_REG_BASE + 0x150)
+
+#define CRG_PEREN7_REG (CRG_REG_BASE + 0x420)
+#define CRG_PERDIS7_REG (CRG_REG_BASE + 0x424)
+#define CRG_PERSTAT7_REG (CRG_REG_BASE + 0x428)
+#define GT_CLK_UFSPHY_CFG (1 << 14)
+
+#define CRG_PEREN8_REG (CRG_REG_BASE + 0x430)
+#define CRG_PERDIS8_REG (CRG_REG_BASE + 0x434)
+#define CRG_PERSTAT8_REG (CRG_REG_BASE + 0x438)
+#define PERI_DMC_D_BIT (1 << 22)
+#define PERI_DMC_C_BIT (1 << 21)
+#define PERI_DMC_B_BIT (1 << 20)
+#define PERI_DMC_A_BIT (1 << 19)
+#define PERI_DMC_BIT (1 << 18)
+
+#define CRG_PEREN11_REG (CRG_REG_BASE + 0x460)
+#define PPLL1_GATE_CPU (1 << 18)
+
+#define CRG_PERSTAT11_REG (CRG_REG_BASE + 0x46C)
+#define PPLL3_EN_STAT (1 << 21)
+#define PPLL2_EN_STAT (1 << 20)
+#define PPLL1_EN_STAT (1 << 19)
+
+#define CRG_IVP_SEC_RSTDIS_REG (CRG_REG_BASE + 0xC04)
+#define CRG_ISP_SEC_RSTDIS_REG (CRG_REG_BASE + 0xC84)
+
+#define CRG_RVBAR(c, n) (0xE00 + (0x10 * c) + (0x4 * n))
+#define CRG_GENERAL_SEC_RSTEN_REG (CRG_REG_BASE + 0xE20)
+#define CRG_GENERAL_SEC_RSTDIS_REG (CRG_REG_BASE + 0xE24)
+#define IP_RST_GPIO0_SEC (1 << 2)
+
+#define CRG_GENERAL_SEC_CLKDIV0_REG (CRG_REG_BASE + 0xE90)
+#define SC_DIV_AO_HISE_MASK 3
+#define SC_DIV_AO_HISE(x) ((x) & 0x3)
+
+#endif /* __HI3660_CRG_H__ */
diff --git a/plat/hisilicon/hikey960/include/hi3660_hkadc.h b/plat/hisilicon/hikey960/include/hi3660_hkadc.h
new file mode 100644
index 00000000..6e67114e
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/hi3660_hkadc.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef __HI3660_HKADC_H__
+#define __HI3660_HKADC_H__
+
+#define HKADC_SSI_REG_BASE 0xE82B8000
+
+#define HKADC_DSP_START_REG (HKADC_SSI_REG_BASE + 0x000)
+#define HKADC_WR_NUM_REG (HKADC_SSI_REG_BASE + 0x008)
+#define HKADC_DSP_START_CLR_REG (HKADC_SSI_REG_BASE + 0x01C)
+#define HKADC_WR01_DATA_REG (HKADC_SSI_REG_BASE + 0x020)
+
+#define WR1_WRITE_MODE (1 << 31)
+#define WR1_READ_MODE (0 << 31)
+#define WR1_ADDR(x) (((x) & 0x7F) << 24)
+#define WR1_DATA(x) (((x) & 0xFF) << 16)
+#define WR0_WRITE_MODE (1 << 15)
+#define WR0_READ_MODE (0 << 15)
+#define WR0_ADDR(x) (((x) & 0x7F) << 8)
+#define WR0_DATA(x) ((x) & 0xFF)
+
+#define HKADC_WR23_DATA_REG (HKADC_SSI_REG_BASE + 0x024)
+#define HKADC_WR45_DATA_REG (HKADC_SSI_REG_BASE + 0x028)
+#define HKADC_DELAY01_REG (HKADC_SSI_REG_BASE + 0x030)
+#define HKADC_DELAY23_REG (HKADC_SSI_REG_BASE + 0x034)
+#define HKADC_DELAY45_REG (HKADC_SSI_REG_BASE + 0x038)
+#define HKADC_DSP_RD2_DATA_REG (HKADC_SSI_REG_BASE + 0x048)
+#define HKADC_DSP_RD3_DATA_REG (HKADC_SSI_REG_BASE + 0x04C)
+
+/* HKADC Internal Registers */
+#define HKADC_CTRL_ADDR 0x00
+#define HKADC_START_ADDR 0x01
+#define HKADC_DATA1_ADDR 0x03 /* high 8 bits */
+#define HKADC_DATA0_ADDR 0x04 /* low 8 bits */
+#define HKADC_MODE_CFG 0x0A
+
+#define HKADC_VALUE_HIGH 0x0FF0
+#define HKADC_VALUE_LOW 0x000F
+#define HKADC_VALID_VALUE 0x0FFF
+
+#define HKADC_CHANNEL_MAX 15
+#define HKADC_VREF_1V8 1800
+#define HKADC_ACCURACY 0x0FFF
+
+#define HKADC_WR01_VALUE ((HKADC_START_ADDR << 24) | \
+ (0x1 << 16))
+#define HKADC_WR23_VALUE ((0x1 << 31) | \
+ (HKADC_DATA0_ADDR << 24) | \
+ (1 << 15) | \
+ (HKADC_DATA1_ADDR << 8))
+#define HKADC_WR45_VALUE (0x80)
+#define HKADC_CHANNEL0_DELAY01_VALUE ((0x0700 << 16) | 0xFFFF)
+#define HKADC_DELAY01_VALUE ((0x0700 << 16) | 0x0200)
+#define HKADC_DELAY23_VALUE ((0x00C8 << 16) | 0x00C8)
+#define START_DELAY_TIMEOUT 2000
+#define HKADC_WR_NUM_VALUE 4
+
+#endif /* __HI3660_HKADC_H__ */
diff --git a/plat/hisilicon/hikey960/include/hi3660_mem_map.h b/plat/hisilicon/hikey960/include/hi3660_mem_map.h
new file mode 100644
index 00000000..db3efaf0
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/hi3660_mem_map.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __HI3660_MEM_MAP__
+#define __HI3660_MEM_MAP__
+
+#define HISI_DATA_HEAD_BASE (0x89C44400)
+
+#define HISI_RESERVED_MEM_BASE (0x89C80000)
+#define HISI_RESERVED_MEM_SIZE (0x00040000)
+
+#define HISI_DATA0_BASE (0x89C96180)
+#define HISI_DATA0_SIZE (0x000003A0)
+#define HISI_DATA1_BASE (0x89C93480)
+#define HISI_DATA1_SIZE (0x00002D00)
+
+#endif /* __HI3660_MEM_MAP__ */
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/include/plat_macros.S b/plat/hisilicon/hikey960/include/plat_macros.S
new file mode 100644
index 00000000..9f1befdc
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/plat_macros.S
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __PLAT_MACROS_S__
+#define __PLAT_MACROS_S__
+
+#include <cci.h>
+#include <hi3660.h>
+#include <gic_v2.h>
+#include <platform_def.h>
+
+.section .rodata.gic_reg_name, "aS"
+gicc_regs:
+ .asciz "gicc_hppir", "gicc_ahppir", "gicc_ctlr", ""
+gicd_pend_reg:
+ .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n" \
+ " Offset:\t\t\tvalue\n"
+newline:
+ .asciz "\n"
+spacer:
+ .asciz ":\t\t0x"
+
+.section .rodata.cci_reg_name, "aS"
+cci_iface_regs:
+ .asciz "cci_snoop_ctrl_cluster0", "cci_snoop_ctrl_cluster1" , ""
+
+/* ---------------------------------------------
+ * The below macro prints out relevant GIC
+ * registers whenever an unhandled exception is
+ * taken in BL31.
+ * ---------------------------------------------
+ */
+.macro plat_crash_print_regs
+ mov_imm x16, GICD_REG_BASE
+ mov_imm x17, GICC_REG_BASE
+
+ /* Load the gicc reg list to x6 */
+ adr x6, gicc_regs
+ /* Load the gicc regs to gp regs used by str_in_crash_buf_print */
+ ldr w8, [x17, #GICC_HPPIR]
+ ldr w9, [x17, #GICC_AHPPIR]
+ ldr w10, [x17, #GICC_CTLR]
+ /* Store to the crash buf and print to cosole */
+ bl str_in_crash_buf_print
+
+ /* Print the GICD_ISPENDR regs */
+ add x7, x16, #GICD_ISPENDR
+ adr x4, gicd_pend_reg
+ bl asm_print_str
+2:
+ sub x4, x7, x16
+ cmp x4, #0x280
+ b.eq 1f
+ bl asm_print_hex
+ adr x4, spacer
+ bl asm_print_str
+ ldr x4, [x7], #8
+ bl asm_print_hex
+ adr x4, newline
+ bl asm_print_str
+ b 2b
+1:
+ adr x6, cci_iface_regs
+ /* Store in x7 the base address of the first interface */
+ mov_imm x7, (CCI400_REG_BASE + SLAVE_IFACE_OFFSET( \
+ CCI400_SL_IFACE3_CLUSTER_IX))
+ ldr w8, [x7, #SNOOP_CTRL_REG]
+ /* Store in x7 the base address of the second interface */
+ mov_imm x7, (CCI400_REG_BASE + SLAVE_IFACE_OFFSET( \
+ CCI400_SL_IFACE4_CLUSTER_IX))
+ ldr w9, [x7, #SNOOP_CTRL_REG]
+ /* Store to the crash buf and print to console */
+ bl str_in_crash_buf_print
+.endm
+
+#endif /* __PLAT_MACROS_S__ */
diff --git a/plat/hisilicon/hikey960/include/platform_def.h b/plat/hisilicon/hikey960/include/platform_def.h
new file mode 100644
index 00000000..369117b8
--- /dev/null
+++ b/plat/hisilicon/hikey960/include/platform_def.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __PLATFORM_DEF_H__
+#define __PLATFORM_DEF_H__
+
+#include <arch.h>
+#include "../hikey960_def.h"
+
+
+/*
+ * Generic platform constants
+ */
+
+/* Size of cacheable stacks */
+#define PLATFORM_STACK_SIZE 0x800
+
+#define FIRMWARE_WELCOME_STR "Booting Trusted Firmware\n"
+
+#define PLATFORM_CACHE_LINE_SIZE 64
+#define PLATFORM_CLUSTER_COUNT 2
+#define PLATFORM_CORE_COUNT_PER_CLUSTER 4
+#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
+ PLATFORM_CORE_COUNT_PER_CLUSTER)
+#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2
+#define PLAT_NUM_PWR_DOMAINS (PLATFORM_CORE_COUNT + \
+ PLATFORM_CLUSTER_COUNT + 1)
+
+#define PLAT_MAX_RET_STATE 1
+#define PLAT_MAX_OFF_STATE 2
+
+#define MAX_IO_DEVICES 3
+#define MAX_IO_HANDLES 4
+/* UFS RPMB and UFS User Data */
+#define MAX_IO_BLOCK_DEVICES 2
+
+
+/*
+ * Platform memory map related constants
+ */
+
+/*
+ * BL1 specific defines.
+ */
+#define BL1_RO_BASE (0x1AC00000)
+#define BL1_RO_LIMIT (BL1_RO_BASE + 0x10000)
+#define BL1_RW_BASE (BL1_RO_LIMIT) /* 1AC1_0000 */
+#define BL1_RW_SIZE (0x00188000)
+#define BL1_RW_LIMIT (0x1B000000)
+
+/*
+ * BL2 specific defines.
+ */
+#define BL2_BASE (BL1_RW_BASE + 0x8000) /* 1AC1_8000 */
+#define BL2_LIMIT (BL2_BASE + 0x40000) /* 1AC5_8000 */
+
+/*
+ * BL31 specific defines.
+ */
+#define BL31_BASE (BL2_LIMIT) /* 1AC5_8000 */
+#define BL31_LIMIT (BL31_BASE + 0x40000) /* 1AC9_8000 */
+
+#define NS_BL1U_BASE (BL31_LIMIT) /* 1AC9_8000 */
+#define NS_BL1U_SIZE (0x00100000)
+#define NS_BL1U_LIMIT (NS_BL1U_BASE + NS_BL1U_SIZE)
+
+#define HIKEY960_NS_IMAGE_OFFSET (0x1AC18000) /* offset in l-loader */
+#define HIKEY960_NS_TMP_OFFSET (0x1AE00000)
+
+#define SCP_BL2_BASE BL31_BASE
+
+#define SCP_MEM_BASE (0x89C80000)
+#define SCP_MEM_SIZE (0x00040000)
+
+/*
+ * Platform specific page table and MMU setup constants
+ */
+#define ADDR_SPACE_SIZE (1ull << 32)
+
+#if IMAGE_BL1 || IMAGE_BL2 || IMAGE_BL31
+#define MAX_XLAT_TABLES 3
+#endif
+
+#define MAX_MMAP_REGIONS 16
+
+/*
+ * Declarations and constants to access the mailboxes safely. Each mailbox is
+ * aligned on the biggest cache line size in the platform. This is known only
+ * to the platform as it might have a combination of integrated and external
+ * caches. Such alignment ensures that two maiboxes do not sit on the same cache
+ * line at any cache level. They could belong to different cpus/clusters &
+ * get written while being protected by different locks causing corruption of
+ * a valid mailbox address.
+ */
+#define CACHE_WRITEBACK_SHIFT 6
+#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)
+
+#endif /* __PLATFORM_DEF_H__ */
diff --git a/plat/hisilicon/hikey960/platform.mk b/plat/hisilicon/hikey960/platform.mk
new file mode 100644
index 00000000..145eee0e
--- /dev/null
+++ b/plat/hisilicon/hikey960/platform.mk
@@ -0,0 +1,65 @@
+#
+# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+CRASH_CONSOLE_BASE := PL011_UART6_BASE
+COLD_BOOT_SINGLE_CPU := 1
+PROGRAMMABLE_RESET_ADDRESS := 1
+
+# Process flags
+$(eval $(call add_define,CRASH_CONSOLE_BASE))
+$(eval $(call FIP_ADD_IMG,SCP_BL2,--scp-fw))
+
+ENABLE_PLAT_COMPAT := 0
+
+USE_COHERENT_MEM := 1
+
+PLAT_INCLUDES := -Iinclude/common/tbbr \
+ -Iplat/hisilicon/hikey960/include
+
+PLAT_BL_COMMON_SOURCES := drivers/arm/pl011/pl011_console.S \
+ drivers/delay_timer/delay_timer.c \
+ drivers/delay_timer/generic_delay_timer.c \
+ lib/aarch64/xlat_tables.c \
+ plat/hisilicon/hikey960/aarch64/hikey960_common.c \
+ plat/hisilicon/hikey960/hikey960_boardid.c
+
+HIKEY960_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
+ drivers/arm/gic/v2/gicv2_main.c \
+ drivers/arm/gic/v2/gicv2_helpers.c \
+ plat/common/plat_gicv2.c
+
+BL1_SOURCES += bl1/tbbr/tbbr_img_desc.c \
+ drivers/io/io_block.c \
+ drivers/io/io_fip.c \
+ drivers/io/io_storage.c \
+ drivers/synopsys/ufs/dw_ufs.c \
+ drivers/ufs/ufs.c \
+ lib/cpus/aarch64/cortex_a53.S \
+ plat/hisilicon/hikey960/aarch64/hikey960_helpers.S \
+ plat/hisilicon/hikey960/hikey960_bl1_setup.c \
+ plat/hisilicon/hikey960/hikey960_io_storage.c \
+ ${HIKEY960_GIC_SOURCES}
+
+BL2_SOURCES += drivers/io/io_block.c \
+ drivers/io/io_fip.c \
+ drivers/io/io_storage.c \
+ drivers/ufs/ufs.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}