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