Add support for Xilinx Zynq UltraScale+ MPSOC

The Xilinx Zynq UltraScale+ MPSOC containes a quad A53 cluster. This
patch adds the platform port for that SoC.

Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c
new file mode 100644
index 0000000..0a878c3
--- /dev/null
+++ b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch_helpers.h>
+#include <cci.h>
+#include <debug.h>
+#include <gicv2.h>
+#include <mmio.h>
+#include <plat_arm.h>
+#include <platform.h>
+#include <xlat_tables.h>
+#include "../zynqmp_private.h"
+
+/*
+ * Table of regions to map using the MMU.
+ * This doesn't include TZRAM as the 'mem_layout' argument passed to
+ * configure_mmu_elx() will give the available subset of that,
+ */
+const mmap_region_t plat_arm_mmap[] = {
+	{ DEVICE0_BASE, DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE },
+	{ DEVICE1_BASE, DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE },
+	{ CRF_APB_BASE, CRF_APB_BASE, CRF_APB_SIZE, MT_DEVICE | MT_RW | MT_SECURE },
+	{0}
+};
+
+static unsigned int zynqmp_get_silicon_ver(void)
+{
+	unsigned int ver;
+
+	ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_VERSION_OFFSET);
+	ver &= ZYNQMP_SILICON_VER_MASK;
+	ver >>= ZYNQMP_SILICON_VER_SHIFT;
+
+	return ver;
+}
+
+unsigned int zynqmp_get_uart_clk(void)
+{
+	unsigned int ver = zynqmp_get_silicon_ver();
+
+	switch (ver) {
+	case ZYNQMP_CSU_VERSION_VELOCE:
+		return 48000;
+	case ZYNQMP_CSU_VERSION_EP108:
+		return 25000000;
+	case ZYNQMP_CSU_VERSION_QEMU:
+		return 133000000;
+	}
+
+	return 100000000;
+}
+
+static unsigned int zynqmp_get_system_timer_freq(void)
+{
+	unsigned int ver = zynqmp_get_silicon_ver();
+
+	switch (ver) {
+	case ZYNQMP_CSU_VERSION_VELOCE:
+		return 10000;
+	case ZYNQMP_CSU_VERSION_EP108:
+		return 4000000;
+	case ZYNQMP_CSU_VERSION_QEMU:
+		return 50000000;
+	}
+
+	return 100000000;
+}
+
+#if LOG_LEVEL >= LOG_LEVEL_NOTICE
+static const struct {
+	unsigned int id;
+	char *name;
+} zynqmp_devices[] = {
+	{
+		.id = 0x10,
+		.name = "3EG",
+	},
+	{
+		.id = 0x11,
+		.name = "2EG",
+	},
+	{
+		.id = 0x20,
+		.name = "5EV",
+	},
+	{
+		.id = 0x21,
+		.name = "4EV",
+	},
+	{
+		.id = 0x30,
+		.name = "7EV",
+	},
+	{
+		.id = 0x38,
+		.name = "9EG",
+	},
+	{
+		.id = 0x39,
+		.name = "6EG",
+	},
+	{
+		.id = 0x40,
+		.name = "11EG",
+	},
+	{
+		.id = 0x50,
+		.name = "15EG",
+	},
+	{
+		.id = 0x58,
+		.name = "19EG",
+	},
+	{
+		.id = 0x59,
+		.name = "17EG",
+	},
+};
+
+static unsigned int zynqmp_get_silicon_id(void)
+{
+	uint32_t id;
+
+	id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET);
+
+	id &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | ZYNQMP_CSU_IDCODE_SVD_MASK;
+	id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT;
+
+	return id;
+}
+
+static char *zynqmp_get_silicon_idcode_name(void)
+{
+	unsigned int id;
+
+	id = zynqmp_get_silicon_id();
+	for (size_t i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) {
+		if (zynqmp_devices[i].id == id)
+			return zynqmp_devices[i].name;
+	}
+	return "UNKN";
+}
+
+static unsigned int zynqmp_get_rtl_ver(void)
+{
+	uint32_t ver;
+
+	ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_VERSION_OFFSET);
+	ver &= ZYNQMP_RTL_VER_MASK;
+	ver >>= ZYNQMP_RTL_VER_SHIFT;
+
+	return ver;
+}
+
+static char *zynqmp_print_silicon_idcode(void)
+{
+	uint32_t id, maskid, tmp;
+
+	id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET);
+
+	tmp = id;
+	tmp &= ZYNQMP_CSU_IDCODE_XILINX_ID_MASK |
+	       ZYNQMP_CSU_IDCODE_FAMILY_MASK |
+	       ZYNQMP_CSU_IDCODE_REVISION_MASK;
+	maskid = ZYNQMP_CSU_IDCODE_XILINX_ID << ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT |
+		 ZYNQMP_CSU_IDCODE_FAMILY << ZYNQMP_CSU_IDCODE_FAMILY_SHIFT |
+		 ZYNQMP_CSU_IDCODE_REVISION << ZYNQMP_CSU_IDCODE_REVISION_SHIFT;
+	if (tmp != maskid) {
+		ERROR("Incorrect XILINX IDCODE 0x%x, maskid 0x%x\n", id, maskid);
+		return "UNKN";
+	}
+	VERBOSE("Xilinx IDCODE 0x%x\n", id);
+	return zynqmp_get_silicon_idcode_name();
+}
+
+static unsigned int zynqmp_get_ps_ver(void)
+{
+	uint32_t ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_VERSION_OFFSET);
+
+	ver &= ZYNQMP_PS_VER_MASK;
+	ver >>= ZYNQMP_PS_VER_SHIFT;
+
+	return ver + 1;
+}
+
+static void zynqmp_print_platform_name(void)
+{
+	unsigned int ver = zynqmp_get_silicon_ver();
+	unsigned int rtl = zynqmp_get_rtl_ver();
+	char *label = "Unknown";
+
+	switch (ver) {
+	case ZYNQMP_CSU_VERSION_VELOCE:
+		label = "VELOCE";
+		break;
+	case ZYNQMP_CSU_VERSION_EP108:
+		label = "EP108";
+		break;
+	case ZYNQMP_CSU_VERSION_QEMU:
+		label = "QEMU";
+		break;
+	case ZYNQMP_CSU_VERSION_SILICON:
+		label = "silicon";
+		break;
+	}
+
+	NOTICE("ATF running on XCZU%s/%s v%d/RTL%d.%d at 0x%x%s\n",
+	       zynqmp_print_silicon_idcode(), label, zynqmp_get_ps_ver(),
+	       (rtl & 0xf0) >> 4, rtl & 0xf, BL31_BASE,
+	       zynqmp_is_pmu_up() ? ", with PMU firmware" : "");
+}
+#else
+static inline void zynqmp_print_platform_name(void) { }
+#endif
+
+/*
+ * Indicator for PMUFW discovery:
+ *   0 = No FW found
+ *   non-zero = FW is present
+ */
+static int zynqmp_pmufw_present;
+
+/*
+ * zynqmp_discover_pmufw - Discover presence of PMUFW
+ *
+ * Discover the presence of PMUFW and store it for later run-time queries
+ * through zynqmp_is_pmu_up.
+ * NOTE: This discovery method is fragile and will break if:
+ *  - setting FW_PRESENT is done by PMUFW itself and could be left out in PMUFW
+ *    (be it by error or intentionally)
+ *  - XPPU/XMPU may restrict ATF's access to the PMU address space
+ */
+static int zynqmp_discover_pmufw(void)
+{
+	zynqmp_pmufw_present = mmio_read_32(PMU_GLOBAL_CNTRL);
+	zynqmp_pmufw_present &= PMU_GLOBAL_CNTRL_FW_IS_PRESENT;
+
+	return !!zynqmp_pmufw_present;
+}
+
+/*
+ * zynqmp_is_pmu_up - Find if PMU firmware is up and running
+ *
+ * Return 0 if firmware is not available, non 0 otherwise
+ */
+int zynqmp_is_pmu_up(void)
+{
+	return zynqmp_pmufw_present;
+}
+
+/*
+ * A single boot loader stack is expected to work on both the Foundation ZYNQMP
+ * models and the two flavours of the Base ZYNQMP models (AEMv8 & Cortex). The
+ * SYS_ID register provides a mechanism for detecting the differences between
+ * these platforms. This information is stored in a per-BL array to allow the
+ * code to take the correct path.Per BL platform configuration.
+ */
+void zynqmp_config_setup(void)
+{
+	zynqmp_discover_pmufw();
+	zynqmp_print_platform_name();
+
+	/* Global timer init - Program time stamp reference clk */
+	uint32_t val = mmio_read_32(CRL_APB_TIMESTAMP_REF_CTRL);
+	val |= CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT;
+	mmio_write_32(CRL_APB_TIMESTAMP_REF_CTRL, val);
+
+	/* Program freq register in System counter and enable system counter. */
+	mmio_write_32(IOU_SCNTRS_BASEFREQ, zynqmp_get_system_timer_freq());
+	mmio_write_32(IOU_SCNTRS_CONTROL, IOU_SCNTRS_CONTROL_EN);
+}
+
+uint64_t plat_get_syscnt_freq(void)
+{
+	uint64_t counter_base_frequency;
+
+	/* FIXME: Read the frequency from Frequency modes table */
+	counter_base_frequency = zynqmp_get_system_timer_freq();
+
+	return counter_base_frequency;
+}