aboutsummaryrefslogtreecommitdiff
path: root/hw/arm/xlnx-versal.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm/xlnx-versal.c')
-rw-r--r--hw/arm/xlnx-versal.c2424
1 files changed, 1792 insertions, 632 deletions
diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c
index a42b9e7..81cb629 100644
--- a/hw/arm/xlnx-versal.c
+++ b/hw/arm/xlnx-versal.c
@@ -1,7 +1,8 @@
/*
- * Xilinx Versal SoC model.
+ * AMD/Xilinx Versal family SoC model.
*
* Copyright (c) 2018 Xilinx Inc.
+ * Copyright (c) 2025 Advanced Micro Devices, Inc.
* Written by Edgar E. Iglesias
*
* This program is free software; you can redistribute it and/or modify
@@ -17,827 +18,1745 @@
#include "hw/sysbus.h"
#include "net/net.h"
#include "system/system.h"
-#include "hw/arm/boot.h"
#include "hw/misc/unimp.h"
#include "hw/arm/xlnx-versal.h"
#include "qemu/log.h"
#include "target/arm/cpu-qom.h"
#include "target/arm/gtimer.h"
+#include "system/device_tree.h"
+#include "hw/arm/fdt.h"
+#include "hw/char/pl011.h"
+#include "hw/net/xlnx-versal-canfd.h"
+#include "hw/sd/sdhci.h"
+#include "hw/net/cadence_gem.h"
+#include "hw/dma/xlnx-zdma.h"
+#include "hw/misc/xlnx-versal-xramc.h"
+#include "hw/usb/xlnx-usb-subsystem.h"
+#include "hw/nvram/xlnx-versal-efuse.h"
+#include "hw/ssi/xlnx-versal-ospi.h"
+#include "hw/misc/xlnx-versal-pmc-iou-slcr.h"
+#include "hw/nvram/xlnx-bbram.h"
+#include "hw/misc/xlnx-versal-trng.h"
+#include "hw/rtc/xlnx-zynqmp-rtc.h"
+#include "hw/misc/xlnx-versal-cfu.h"
+#include "hw/misc/xlnx-versal-cframe-reg.h"
+#include "hw/or-irq.h"
+#include "hw/misc/xlnx-versal-crl.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "hw/intc/arm_gicv3_its_common.h"
+#include "hw/intc/arm_gic.h"
+#include "hw/core/split-irq.h"
+#include "target/arm/cpu.h"
+#include "hw/cpu/cluster.h"
+#include "hw/arm/bsa.h"
-#define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72")
-#define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f")
-#define GEM_REVISION 0x40070106
+/*
+ * IRQ descriptor to catch the following cases:
+ * - An IRQ can either connect to the GICs, to the PPU1 intc, or the the EAM
+ * - Multiple devices can connect to the same IRQ. They are OR'ed together.
+ */
+FIELD(VERSAL_IRQ, IRQ, 0, 16)
+FIELD(VERSAL_IRQ, TARGET, 16, 2)
+FIELD(VERSAL_IRQ, ORED, 18, 1)
+FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */
+
+typedef enum VersalIrqTarget {
+ IRQ_TARGET_GIC,
+ IRQ_TARGET_PPU1,
+ IRQ_TARGET_EAM,
+} VersalIrqTarget;
+
+#define PPU1_IRQ(irq) ((IRQ_TARGET_PPU1 << R_VERSAL_IRQ_TARGET_SHIFT) | (irq))
+#define EAM_IRQ(irq) ((IRQ_TARGET_EAM << R_VERSAL_IRQ_TARGET_SHIFT) | (irq))
+#define OR_IRQ(irq, or_idx) \
+ (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq))
+#define PPU1_OR_IRQ(irq, or_idx) \
+ ((IRQ_TARGET_PPU1 << R_VERSAL_IRQ_TARGET_SHIFT) | OR_IRQ(irq, or_idx))
+
+typedef struct VersalSimplePeriphMap {
+ uint64_t addr;
+ int irq;
+} VersalSimplePeriphMap;
+
+typedef struct VersalMemMap {
+ uint64_t addr;
+ uint64_t size;
+} VersalMemMap;
+
+typedef struct VersalGicMap {
+ int version;
+ uint64_t dist;
+ uint64_t redist;
+ uint64_t cpu_iface;
+ uint64_t its;
+ size_t num_irq;
+ bool has_its;
+} VersalGicMap;
+
+enum StartPoweredOffMode {
+ SPO_SECONDARIES,
+ SPO_ALL,
+};
+
+typedef struct VersalCpuClusterMap {
+ VersalGicMap gic;
+ /*
+ * true: one GIC per cluster.
+ * false: one GIC for all CPUs
+ */
+ bool per_cluster_gic;
+
+ const char *name;
+ const char *cpu_model;
+ size_t num_core;
+ size_t num_cluster;
+ uint32_t qemu_cluster_id;
+ bool dtb_expose;
-#define VERSAL_NUM_PMC_APB_IRQS 18
-#define NUM_OSPI_IRQ_LINES 3
+ struct {
+ uint64_t base;
+ uint64_t core_shift;
+ uint64_t cluster_shift;
+ } mp_affinity;
+
+ enum StartPoweredOffMode start_powered_off;
+} VersalCpuClusterMap;
+
+typedef struct VersalMap {
+ VersalMemMap ocm;
+
+ struct VersalDDRMap {
+ VersalMemMap chan[4];
+ size_t num_chan;
+ } ddr;
+
+ VersalCpuClusterMap apu;
+ VersalCpuClusterMap rpu;
+
+ VersalSimplePeriphMap uart[2];
+ size_t num_uart;
+
+ VersalSimplePeriphMap canfd[4];
+ size_t num_canfd;
+
+ VersalSimplePeriphMap sdhci[2];
+ size_t num_sdhci;
+
+ struct VersalGemMap {
+ VersalSimplePeriphMap map;
+ size_t num_prio_queue;
+ const char *phy_mode;
+ const uint32_t speed;
+ } gem[3];
+ size_t num_gem;
+
+ struct VersalZDMAMap {
+ const char *name;
+ VersalSimplePeriphMap map;
+ size_t num_chan;
+ uint64_t chan_stride;
+ int irq_stride;
+ } zdma[2];
+ size_t num_zdma;
+
+ struct VersalXramMap {
+ uint64_t mem;
+ uint64_t mem_stride;
+ uint64_t ctrl;
+ uint64_t ctrl_stride;
+ int irq;
+ size_t num;
+ } xram;
+
+ struct VersalUsbMap {
+ uint64_t xhci;
+ uint64_t ctrl;
+ int irq;
+ } usb[2];
+ size_t num_usb;
+
+ struct VersalEfuseMap {
+ uint64_t ctrl;
+ uint64_t cache;
+ int irq;
+ } efuse;
+
+ struct VersalOspiMap {
+ uint64_t ctrl;
+ uint64_t dac;
+ uint64_t dac_sz;
+ uint64_t dma_src;
+ uint64_t dma_dst;
+ int irq;
+ } ospi;
+
+ VersalSimplePeriphMap pmc_iou_slcr;
+ VersalSimplePeriphMap bbram;
+ VersalSimplePeriphMap trng;
+
+ struct VersalRtcMap {
+ VersalSimplePeriphMap map;
+ int alarm_irq;
+ int second_irq;
+ } rtc;
+
+ struct VersalCfuMap {
+ uint64_t cframe_base;
+ uint64_t cframe_stride;
+ uint64_t cfu_fdro;
+ uint64_t cframe_bcast_reg;
+ uint64_t cframe_bcast_fdri;
+ uint64_t cfu_apb;
+ uint64_t cfu_stream;
+ uint64_t cfu_stream_2;
+ uint64_t cfu_sfr;
+ int cfu_apb_irq;
+ int cframe_irq;
+ size_t num_cframe;
+ struct VersalCfuCframeCfg {
+ uint32_t blktype_frames[7];
+ } cframe_cfg[15];
+ } cfu;
+
+ VersalSimplePeriphMap crl;
+
+ /* reserved MMIO/IRQ space that can safely be used for virtio devices */
+ struct VersalReserved {
+ uint64_t mmio_start;
+ int irq_start;
+ int irq_num;
+ } reserved;
+} VersalMap;
+
+static const VersalMap VERSAL_MAP = {
+ .ocm = {
+ .addr = 0xfffc0000,
+ .size = 0x40000,
+ },
+
+ .ddr = {
+ .chan[0] = { .addr = 0x0, .size = 2 * GiB },
+ .chan[1] = { .addr = 0x800000000ull, .size = 32 * GiB },
+ .chan[2] = { .addr = 0xc00000000ull, .size = 256 * GiB },
+ .chan[3] = { .addr = 0x10000000000ull, .size = 734 * GiB },
+ .num_chan = 4,
+ },
+
+ .apu = {
+ .name = "apu",
+ .cpu_model = ARM_CPU_TYPE_NAME("cortex-a72"),
+ .num_cluster = 1,
+ .num_core = 2,
+ .qemu_cluster_id = 0,
+ .mp_affinity = {
+ .core_shift = ARM_AFF0_SHIFT,
+ .cluster_shift = ARM_AFF1_SHIFT,
+ },
+ .start_powered_off = SPO_SECONDARIES,
+ .dtb_expose = true,
+ .gic = {
+ .version = 3,
+ .dist = 0xf9000000,
+ .redist = 0xf9080000,
+ .num_irq = 192,
+ .has_its = true,
+ .its = 0xf9020000,
+ },
+ },
+
+ .rpu = {
+ .name = "rpu",
+ .cpu_model = ARM_CPU_TYPE_NAME("cortex-r5f"),
+ .num_cluster = 1,
+ .num_core = 2,
+ .qemu_cluster_id = 1,
+ .mp_affinity = {
+ .base = 0x100,
+ .core_shift = ARM_AFF0_SHIFT,
+ .cluster_shift = ARM_AFF1_SHIFT,
+ },
+ .start_powered_off = SPO_ALL,
+ .dtb_expose = false,
+ .gic = {
+ .version = 2,
+ .dist = 0xf9000000,
+ .cpu_iface = 0xf9001000,
+ .num_irq = 192,
+ },
+ },
+
+ .uart[0] = { 0xff000000, 18 },
+ .uart[1] = { 0xff010000, 19 },
+ .num_uart = 2,
+
+ .canfd[0] = { 0xff060000, 20 },
+ .canfd[1] = { 0xff070000, 21 },
+ .num_canfd = 2,
+
+ .sdhci[0] = { 0xf1040000, 126 },
+ .sdhci[1] = { 0xf1050000, 128 },
+ .num_sdhci = 2,
+
+ .gem[0] = { { 0xff0c0000, 56 }, 2, "rgmii-id", 1000 },
+ .gem[1] = { { 0xff0d0000, 58 }, 2, "rgmii-id", 1000 },
+ .num_gem = 2,
+
+ .zdma[0] = { "adma", { 0xffa80000, 60 }, 8, 0x10000, 1 },
+ .num_zdma = 1,
+
+ .xram = {
+ .num = 4,
+ .mem = 0xfe800000, .mem_stride = 1 * MiB,
+ .ctrl = 0xff8e0000, .ctrl_stride = 0x10000,
+ .irq = 79,
+ },
+
+ .usb[0] = { .xhci = 0xfe200000, .ctrl = 0xff9d0000, .irq = 22 },
+ .num_usb = 1,
+
+ .efuse = { .ctrl = 0xf1240000, .cache = 0xf1250000, .irq = 139 },
+
+ .ospi = {
+ .ctrl = 0xf1010000,
+ .dac = 0xc0000000, .dac_sz = 0x20000000,
+ .dma_src = 0xf1011000, .dma_dst = 0xf1011800,
+ .irq = 124,
+ },
+
+ .pmc_iou_slcr = { 0xf1060000, OR_IRQ(121, 0) },
+ .bbram = { 0xf11f0000, OR_IRQ(121, 1) },
+ .trng = { 0xf1230000, 141 },
+ .rtc = {
+ { 0xf12a0000, OR_IRQ(121, 2) },
+ .alarm_irq = 142, .second_irq = 143
+ },
+
+ .cfu = {
+ .cframe_base = 0xf12d0000, .cframe_stride = 0x1000,
+ .cframe_bcast_reg = 0xf12ee000, .cframe_bcast_fdri = 0xf12ef000,
+ .cfu_apb = 0xf12b0000, .cfu_sfr = 0xf12c1000,
+ .cfu_stream = 0xf12c0000, .cfu_stream_2 = 0xf1f80000,
+ .cfu_fdro = 0xf12c2000,
+ .cfu_apb_irq = 120, .cframe_irq = OR_IRQ(121, 3),
+ .num_cframe = 15,
+ .cframe_cfg = {
+ { { 34111, 3528, 12800, 11, 5, 1, 1 } },
+ { { 38498, 3841, 15361, 13, 7, 3, 1 } },
+ { { 38498, 3841, 15361, 13, 7, 3, 1 } },
+ { { 38498, 3841, 15361, 13, 7, 3, 1 } },
+ },
+ },
+
+ .crl = { 0xff5e0000, 10 },
+
+ .reserved = { 0xa0000000, 111, 8 },
+};
+
+static const VersalMap VERSAL2_MAP = {
+ .ocm = {
+ .addr = 0xbbe00000,
+ .size = 2 * MiB,
+ },
+
+ .ddr = {
+ .chan[0] = { .addr = 0x0, .size = 2046 * MiB },
+ .chan[1] = { .addr = 0x800000000ull, .size = 32 * GiB },
+ .chan[2] = { .addr = 0xc00000000ull, .size = 256 * GiB },
+ .chan[3] = { .addr = 0x10000000000ull, .size = 734 * GiB },
+ .num_chan = 4,
+ },
+
+ .apu = {
+ .name = "apu",
+ .cpu_model = ARM_CPU_TYPE_NAME("cortex-a78ae"),
+ .num_cluster = 4,
+ .num_core = 2,
+ .qemu_cluster_id = 0,
+ .mp_affinity = {
+ .base = 0x0, /* TODO: the MT bit should be set */
+ .core_shift = ARM_AFF1_SHIFT,
+ .cluster_shift = ARM_AFF2_SHIFT,
+ },
+ .start_powered_off = SPO_SECONDARIES,
+ .dtb_expose = true,
+ .gic = {
+ .version = 3,
+ .dist = 0xe2000000,
+ .redist = 0xe2060000,
+ .num_irq = 544,
+ .has_its = true,
+ .its = 0xe2040000,
+ },
+ },
+
+ .rpu = {
+ .name = "rpu",
+ .cpu_model = ARM_CPU_TYPE_NAME("cortex-r52"),
+ .num_cluster = 5,
+ .num_core = 2,
+ .qemu_cluster_id = 1,
+ .mp_affinity = {
+ .core_shift = ARM_AFF0_SHIFT,
+ .cluster_shift = ARM_AFF1_SHIFT,
+ },
+ .start_powered_off = SPO_ALL,
+ .dtb_expose = false,
+ .per_cluster_gic = true,
+ .gic = {
+ .version = 3,
+ .dist = 0x0,
+ .redist = 0x100000,
+ .num_irq = 288,
+ },
+ },
+
+ .uart[0] = { 0xf1920000, 25 },
+ .uart[1] = { 0xf1930000, 26 },
+ .num_uart = 2,
+
+ .canfd[0] = { 0xf19e0000, 27 },
+ .canfd[1] = { 0xf19f0000, 28 },
+ .canfd[2] = { 0xf1a00000, 95 },
+ .canfd[3] = { 0xf1a10000, 96 },
+ .num_canfd = 4,
+
+ .gem[0] = { { 0xf1a60000, 39 }, 2, "rgmii-id", 1000 },
+ .gem[1] = { { 0xf1a70000, 41 }, 2, "rgmii-id", 1000 },
+ .gem[2] = { { 0xed920000, 164 }, 4, "usxgmii", 10000 }, /* MMI 10Gb GEM */
+ .num_gem = 3,
+
+ .zdma[0] = { "adma", { 0xebd00000, 72 }, 8, 0x10000, 1 },
+ .zdma[1] = { "sdma", { 0xebd80000, 112 }, 8, 0x10000, 1 },
+ .num_zdma = 2,
+
+ .usb[0] = { .xhci = 0xf1b00000, .ctrl = 0xf1ee0000, .irq = 29 },
+ .usb[1] = { .xhci = 0xf1c00000, .ctrl = 0xf1ef0000, .irq = 34 },
+ .num_usb = 2,
+
+ .efuse = { .ctrl = 0xf1240000, .cache = 0xf1250000, .irq = 230 },
+
+ .ospi = {
+ .ctrl = 0xf1010000,
+ .dac = 0xc0000000, .dac_sz = 0x20000000,
+ .dma_src = 0xf1011000, .dma_dst = 0xf1011800,
+ .irq = 216,
+ },
+
+ .sdhci[0] = { 0xf1040000, 218 },
+ .sdhci[1] = { 0xf1050000, 220 }, /* eMMC */
+ .num_sdhci = 2,
+
+ .pmc_iou_slcr = { 0xf1060000, 222 },
+ .bbram = { 0xf11f0000, PPU1_OR_IRQ(18, 0) },
+ .crl = { 0xeb5e0000 },
+ .trng = { 0xf1230000, 233 },
+ .rtc = {
+ { 0xf12a0000, PPU1_OR_IRQ(18, 1) },
+ .alarm_irq = 200, .second_irq = 201
+ },
+
+ .cfu = {
+ .cframe_base = 0xf12d0000, .cframe_stride = 0x1000,
+ .cframe_bcast_reg = 0xf12ee000, .cframe_bcast_fdri = 0xf12ef000,
+ .cfu_apb = 0xf12b0000, .cfu_sfr = 0xf12c1000,
+ .cfu_stream = 0xf12c0000, .cfu_stream_2 = 0xf1f80000,
+ .cfu_fdro = 0xf12c2000,
+ .cfu_apb_irq = 235, .cframe_irq = EAM_IRQ(7),
+ },
+
+ .reserved = { 0xf5e00000, 270, 8 },
+};
-static void versal_create_apu_cpus(Versal *s)
+static const VersalMap *VERSION_TO_MAP[] = {
+ [VERSAL_VER_VERSAL] = &VERSAL_MAP,
+ [VERSAL_VER_VERSAL2] = &VERSAL2_MAP,
+};
+
+static inline VersalVersion versal_get_version(Versal *s)
{
- int i;
+ return XLNX_VERSAL_BASE_GET_CLASS(s)->version;
+}
- object_initialize_child(OBJECT(s), "apu-cluster", &s->fpd.apu.cluster,
- TYPE_CPU_CLUSTER);
- qdev_prop_set_uint32(DEVICE(&s->fpd.apu.cluster), "cluster-id", 0);
-
- for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) {
- Object *obj;
-
- object_initialize_child(OBJECT(&s->fpd.apu.cluster),
- "apu-cpu[*]", &s->fpd.apu.cpu[i],
- XLNX_VERSAL_ACPU_TYPE);
- obj = OBJECT(&s->fpd.apu.cpu[i]);
- if (i) {
- /* Secondary CPUs start in powered-down state */
- object_property_set_bool(obj, "start-powered-off", true,
- &error_abort);
- }
+static inline const VersalMap *versal_get_map(Versal *s)
+{
+ return VERSION_TO_MAP[versal_get_version(s)];
+}
- object_property_set_int(obj, "core-count", ARRAY_SIZE(s->fpd.apu.cpu),
- &error_abort);
- object_property_set_link(obj, "memory", OBJECT(&s->fpd.apu.mr),
- &error_abort);
- qdev_realize(DEVICE(obj), NULL, &error_fatal);
+static inline Object *versal_get_child(Versal *s, const char *child)
+{
+ return object_resolve_path_at(OBJECT(s), child);
+}
+
+static inline Object *versal_get_child_idx(Versal *s, const char *child,
+ size_t idx)
+{
+ g_autofree char *n = g_strdup_printf("%s[%zu]", child, idx);
+
+ return versal_get_child(s, n);
+}
+
+/*
+ * The SoC embeds multiple GICs. They all receives the same IRQ lines at the
+ * same index. This function creates a TYPE_SPLIT_IRQ device to fan out the
+ * given IRQ input to all the GICs.
+ *
+ * The TYPE_SPLIT_IRQ devices lie in the /soc/irq-splits QOM container
+ */
+static qemu_irq versal_get_gic_irq(Versal *s, int irq_idx)
+{
+ DeviceState *split;
+ Object *container = versal_get_child(s, "irq-splits");
+ int idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
+ g_autofree char *name = g_strdup_printf("irq[%d]", idx);
+
+ split = DEVICE(object_resolve_path_at(container, name));
+
+ if (split == NULL) {
+ size_t i;
+
+ split = qdev_new(TYPE_SPLIT_IRQ);
+ qdev_prop_set_uint16(split, "num-lines", s->intc->len);
+ object_property_add_child(container, name, OBJECT(split));
+ qdev_realize_and_unref(split, NULL, &error_abort);
+
+ for (i = 0; i < s->intc->len; i++) {
+ DeviceState *gic;
+
+ gic = g_array_index(s->intc, DeviceState *, i);
+ qdev_connect_gpio_out(split, i, qdev_get_gpio_in(gic, idx));
+ }
+ } else {
+ g_assert(FIELD_EX32(irq_idx, VERSAL_IRQ, ORED));
}
- qdev_realize(DEVICE(&s->fpd.apu.cluster), NULL, &error_fatal);
+ return qdev_get_gpio_in(split, 0);
}
-static void versal_create_apu_gic(Versal *s, qemu_irq *pic)
+/*
+ * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is
+ * used to return the corresponding or gate input IRQ. The or gate is created if
+ * not already existant.
+ *
+ * Or gates are placed under the /soc/irq-or-gates QOM container.
+ */
+static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
+ qemu_irq target_irq)
{
- static const uint64_t addrs[] = {
- MM_GIC_APU_DIST_MAIN,
- MM_GIC_APU_REDIST_0
+ static const char *TARGET_STR[] = {
+ [IRQ_TARGET_GIC] = "gic",
+ [IRQ_TARGET_PPU1] = "ppu1",
+ [IRQ_TARGET_EAM] = "eam",
};
- SysBusDevice *gicbusdev;
- DeviceState *gicdev;
- QList *redist_region_count;
- int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu);
- int i;
- object_initialize_child(OBJECT(s), "apu-gic", &s->fpd.apu.gic,
- gicv3_class_name());
- gicbusdev = SYS_BUS_DEVICE(&s->fpd.apu.gic);
- gicdev = DEVICE(&s->fpd.apu.gic);
- qdev_prop_set_uint32(gicdev, "revision", 3);
- qdev_prop_set_uint32(gicdev, "num-cpu", nr_apu_cpus);
- qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32);
+ VersalIrqTarget target;
+ Object *container = versal_get_child(s, "irq-or-gates");
+ DeviceState *dev;
+ g_autofree char *name;
+ int idx, or_idx;
+
+ idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
+ or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
+ target = FIELD_EX32(irq_idx, VERSAL_IRQ, TARGET);
+
+ name = g_strdup_printf("%s-irq[%d]", TARGET_STR[target], idx);
+ dev = DEVICE(object_resolve_path_at(container, name));
+
+ if (dev == NULL) {
+ dev = qdev_new(TYPE_OR_IRQ);
+ object_property_add_child(container, name, OBJECT(dev));
+ qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
+ qdev_realize_and_unref(dev, NULL, &error_abort);
+ qdev_connect_gpio_out(dev, 0, target_irq);
+ }
- redist_region_count = qlist_new();
- qlist_append_int(redist_region_count, nr_apu_cpus);
- qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count);
+ return qdev_get_gpio_in(dev, or_idx);
+}
+
+static qemu_irq versal_get_irq(Versal *s, int irq_idx)
+{
+ VersalIrqTarget target;
+ qemu_irq irq;
+ bool ored;
+
+ target = FIELD_EX32(irq_idx, VERSAL_IRQ, TARGET);
+ ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED);
- qdev_prop_set_bit(gicdev, "has-security-extensions", true);
+ switch (target) {
+ case IRQ_TARGET_EAM:
+ /* EAM not implemented */
+ return NULL;
- sysbus_realize(SYS_BUS_DEVICE(&s->fpd.apu.gic), &error_fatal);
+ case IRQ_TARGET_PPU1:
+ /* PPU1 CPU not implemented */
+ return NULL;
- for (i = 0; i < ARRAY_SIZE(addrs); i++) {
- MemoryRegion *mr;
+ case IRQ_TARGET_GIC:
+ irq = versal_get_gic_irq(s, irq_idx);
+ break;
- mr = sysbus_mmio_get_region(gicbusdev, i);
- memory_region_add_subregion(&s->fpd.apu.mr, addrs[i], mr);
+ default:
+ g_assert_not_reached();
}
- for (i = 0; i < nr_apu_cpus; i++) {
- DeviceState *cpudev = DEVICE(&s->fpd.apu.cpu[i]);
- int ppibase = XLNX_VERSAL_NR_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
- qemu_irq maint_irq;
- int ti;
- /* Mapping from the output timer irq lines from the CPU to the
- * GIC PPI inputs.
- */
- const int timer_irq[] = {
- [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ,
- [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ,
- [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ,
- [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ,
- };
+ if (ored) {
+ irq = versal_get_irq_or_gate_in(s, irq_idx, irq);
+ }
+
+ return irq;
+}
+
+static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd,
+ int sbd_idx, int irq_idx)
+{
+ qemu_irq irq = versal_get_irq(s, irq_idx);
+
+ if (irq == NULL) {
+ return;
+ }
+
+ sysbus_connect_irq(sbd, sbd_idx, irq);
+}
+
+static void versal_qdev_connect_gpio_out(Versal *s, DeviceState *dev,
+ int dev_idx, int irq_idx)
+{
+ qemu_irq irq = versal_get_irq(s, irq_idx);
+
+ if (irq == NULL) {
+ return;
+ }
+
+ qdev_connect_gpio_out(dev, dev_idx, irq);
+}
+
+static inline char *versal_fdt_add_subnode(Versal *s, const char *path,
+ uint64_t at, const char *compat,
+ size_t compat_sz)
+{
+ char *p;
+
+ p = g_strdup_printf("%s@%" PRIx64, path, at);
+ qemu_fdt_add_subnode(s->cfg.fdt, p);
+
+ if (!strncmp(compat, "memory", compat_sz)) {
+ qemu_fdt_setprop(s->cfg.fdt, p, "device_type", compat, compat_sz);
+ } else {
+ qemu_fdt_setprop(s->cfg.fdt, p, "compatible", compat, compat_sz);
+ }
+
+ return p;
+}
+
+static inline char *versal_fdt_add_simple_subnode(Versal *s, const char *path,
+ uint64_t addr, uint64_t len,
+ const char *compat,
+ size_t compat_sz)
+{
+ char *p = versal_fdt_add_subnode(s, path, addr, compat, compat_sz);
+
+ qemu_fdt_setprop_sized_cells(s->cfg.fdt, p, "reg", 2, addr, 2, len);
+ return p;
+}
+
+static inline DeviceState *create_or_gate(Versal *s, Object *parent,
+ const char *name, uint16_t num_lines,
+ int irq_idx)
+{
+ DeviceState *or;
+
+ or = qdev_new(TYPE_OR_IRQ);
+ qdev_prop_set_uint16(or, "num-lines", num_lines);
+ object_property_add_child(parent, name, OBJECT(or));
+ qdev_realize_and_unref(or, NULL, &error_abort);
+ versal_qdev_connect_gpio_out(s, or, 0, irq_idx);
+
+ return or;
+}
+
+static MemoryRegion *create_cpu_mr(Versal *s, DeviceState *cluster,
+ const VersalCpuClusterMap *map)
+{
+ MemoryRegion *mr, *root_alias;
+ char *name;
+
+ mr = g_new(MemoryRegion, 1);
+ name = g_strdup_printf("%s-mr", map->name);
+ memory_region_init(mr, OBJECT(cluster), name, UINT64_MAX);
+ g_free(name);
+
+ root_alias = g_new(MemoryRegion, 1);
+ name = g_strdup_printf("ps-alias-for-%s", map->name);
+ memory_region_init_alias(root_alias, OBJECT(cluster), name,
+ &s->mr_ps, 0, UINT64_MAX);
+ g_free(name);
+ memory_region_add_subregion(mr, 0, root_alias);
+
+ return mr;
+}
+
+static void versal_create_gic_its(Versal *s,
+ const VersalCpuClusterMap *map,
+ DeviceState *gic,
+ MemoryRegion *mr,
+ char *gic_node)
+{
+ DeviceState *dev;
+ SysBusDevice *sbd;
+ g_autofree char *node_pat = NULL, *node = NULL;
+ const char compatible[] = "arm,gic-v3-its";
+
+ if (map->gic.version != 3) {
+ return;
+ }
+
+ if (!map->gic.has_its) {
+ return;
+ }
+
+ dev = qdev_new(TYPE_ARM_GICV3_ITS);
+ sbd = SYS_BUS_DEVICE(dev);
+
+ object_property_add_child(OBJECT(gic), "its", OBJECT(dev));
+ object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(gic),
+ &error_abort);
+
+ sysbus_realize_and_unref(sbd, &error_abort);
+
+ memory_region_add_subregion(mr, map->gic.its,
+ sysbus_mmio_get_region(sbd, 0));
+
+ if (!map->dtb_expose) {
+ return;
+ }
+
+ qemu_fdt_setprop(s->cfg.fdt, gic_node, "ranges", NULL, 0);
+ qemu_fdt_setprop_cell(s->cfg.fdt, gic_node, "#address-cells", 2);
+ qemu_fdt_setprop_cell(s->cfg.fdt, gic_node, "#size-cells", 2);
+
+ node_pat = g_strdup_printf("%s/its", gic_node);
+ node = versal_fdt_add_simple_subnode(s, node_pat, map->gic.its, 0x20000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop(s->cfg.fdt, node, "msi-controller", NULL, 0);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "#msi-cells", 1);
+}
+
+static DeviceState *versal_create_gic(Versal *s,
+ const VersalCpuClusterMap *map,
+ MemoryRegion *mr,
+ int first_cpu_idx,
+ size_t num_cpu)
+{
+ DeviceState *dev;
+ SysBusDevice *sbd;
+ g_autofree char *node = NULL;
+ g_autofree char *name = NULL;
+ const char gicv3_compat[] = "arm,gic-v3";
+ const char gicv2_compat[] = "arm,cortex-a15-gic";
+
+ switch (map->gic.version) {
+ case 2:
+ dev = qdev_new(gic_class_name());
+ break;
+
+ case 3:
+ dev = qdev_new(gicv3_class_name());
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ name = g_strdup_printf("%s-gic[*]", map->name);
+ object_property_add_child(OBJECT(s), name, OBJECT(dev));
+ sbd = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_uint32(dev, "revision", map->gic.version);
+ qdev_prop_set_uint32(dev, "num-cpu", num_cpu);
+ qdev_prop_set_uint32(dev, "num-irq", map->gic.num_irq + 32);
+ qdev_prop_set_bit(dev, "has-security-extensions", true);
+ qdev_prop_set_uint32(dev, "first-cpu-index", first_cpu_idx);
+
+ if (map->gic.version == 3) {
+ QList *redist_region_count;
+
+ redist_region_count = qlist_new();
+ qlist_append_int(redist_region_count, num_cpu);
+ qdev_prop_set_array(dev, "redist-region-count", redist_region_count);
+ qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its);
+ object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr),
+ &error_abort);
+ }
+
+ sysbus_realize_and_unref(sbd, &error_fatal);
+
+ memory_region_add_subregion(mr, map->gic.dist,
+ sysbus_mmio_get_region(sbd, 0));
+
+ if (map->gic.version == 3) {
+ memory_region_add_subregion(mr, map->gic.redist,
+ sysbus_mmio_get_region(sbd, 1));
+ } else {
+ memory_region_add_subregion(mr, map->gic.cpu_iface,
+ sysbus_mmio_get_region(sbd, 1));
+ }
+
+ if (map->dtb_expose) {
+ if (map->gic.version == 3) {
+ node = versal_fdt_add_subnode(s, "/gic", map->gic.dist,
+ gicv3_compat,
+ sizeof(gicv3_compat));
+ qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg",
+ 2, map->gic.dist,
+ 2, 0x10000,
+ 2, map->gic.redist,
+ 2, GICV3_REDIST_SIZE * num_cpu);
+ } else {
+ node = versal_fdt_add_subnode(s, "/gic", map->gic.dist,
+ gicv2_compat,
+ sizeof(gicv2_compat));
+ qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg",
+ 2, map->gic.dist,
+ 2, 0x1000,
+ 2, map->gic.cpu_iface,
+ 2, 0x1000);
+ }
+
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "phandle", s->phandle.gic);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "#interrupt-cells", 3);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_PPI,
+ INTID_TO_PPI(ARCH_GIC_MAINT_IRQ),
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-controller", NULL, 0);
+ }
+
+ versal_create_gic_its(s, map, dev, mr, node);
+
+ g_array_append_val(s->intc, dev);
+
+ return dev;
+}
+
+static void connect_gic_to_cpu(const VersalCpuClusterMap *map,
+ DeviceState *gic, DeviceState *cpu, size_t idx,
+ size_t num_cpu)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(gic);
+ int ppibase = map->gic.num_irq + idx * GIC_INTERNAL + GIC_NR_SGIS;
+ int ti;
+ bool has_gtimer;
+ /*
+ * Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs.
+ */
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ),
+ [GTIMER_VIRT] = INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ),
+ [GTIMER_HYP] = INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ),
+ [GTIMER_SEC] = INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ),
+ };
+
+ has_gtimer = arm_feature(&ARM_CPU(cpu)->env, ARM_FEATURE_GENERIC_TIMER);
+
+ if (has_gtimer) {
for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) {
- qdev_connect_gpio_out(cpudev, ti,
- qdev_get_gpio_in(gicdev,
+ qdev_connect_gpio_out(cpu, ti,
+ qdev_get_gpio_in(gic,
ppibase + timer_irq[ti]));
}
- maint_irq = qdev_get_gpio_in(gicdev,
- ppibase + VERSAL_GIC_MAINT_IRQ);
- qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
- 0, maint_irq);
- sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
- sysbus_connect_irq(gicbusdev, i + nr_apu_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
- sysbus_connect_irq(gicbusdev, i + 2 * nr_apu_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
- sysbus_connect_irq(gicbusdev, i + 3 * nr_apu_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
}
- for (i = 0; i < XLNX_VERSAL_NR_IRQS; i++) {
- pic[i] = qdev_get_gpio_in(gicdev, i);
+ if (map->gic.version == 3) {
+ qemu_irq maint_irq;
+ int maint_idx = ppibase + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ);
+
+ maint_irq = qdev_get_gpio_in(gic, maint_idx);
+ qdev_connect_gpio_out_named(cpu, "gicv3-maintenance-interrupt",
+ 0, maint_irq);
}
+
+ sysbus_connect_irq(sbd, idx, qdev_get_gpio_in(cpu, ARM_CPU_IRQ));
+ sysbus_connect_irq(sbd, idx + num_cpu,
+ qdev_get_gpio_in(cpu, ARM_CPU_FIQ));
+ sysbus_connect_irq(sbd, idx + 2 * num_cpu,
+ qdev_get_gpio_in(cpu, ARM_CPU_VIRQ));
+ sysbus_connect_irq(sbd, idx + 3 * num_cpu,
+ qdev_get_gpio_in(cpu, ARM_CPU_VFIQ));
}
-static void versal_create_rpu_cpus(Versal *s)
+static inline void versal_create_and_connect_gic(Versal *s,
+ const VersalCpuClusterMap *map,
+ MemoryRegion *mr,
+ DeviceState **cpus,
+ size_t num_cpu)
{
- int i;
+ DeviceState *gic;
+ int first_cpu_idx;
+ size_t i;
- object_initialize_child(OBJECT(s), "rpu-cluster", &s->lpd.rpu.cluster,
- TYPE_CPU_CLUSTER);
- qdev_prop_set_uint32(DEVICE(&s->lpd.rpu.cluster), "cluster-id", 1);
+ first_cpu_idx = CPU(cpus[0])->cpu_index;
+ gic = versal_create_gic(s, map, mr, first_cpu_idx, num_cpu);
- for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) {
- Object *obj;
+ for (i = 0; i < num_cpu; i++) {
+ connect_gic_to_cpu(map, gic, cpus[i], i, num_cpu);
+ }
+}
- object_initialize_child(OBJECT(&s->lpd.rpu.cluster),
- "rpu-cpu[*]", &s->lpd.rpu.cpu[i],
- XLNX_VERSAL_RCPU_TYPE);
- obj = OBJECT(&s->lpd.rpu.cpu[i]);
- object_property_set_bool(obj, "start-powered-off", true,
- &error_abort);
+static DeviceState *versal_create_cpu(Versal *s,
+ const VersalCpuClusterMap *map,
+ DeviceState *qemu_cluster,
+ MemoryRegion *cpu_mr,
+ size_t cluster_idx,
+ size_t core_idx)
+{
+ DeviceState *cpu = qdev_new(map->cpu_model);
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ Object *obj = OBJECT(cpu);
+ uint64_t affinity;
+ bool start_off;
+ size_t idx = cluster_idx * map->num_core + core_idx;
+ g_autofree char *name;
+ g_autofree char *node = NULL;
+
+ affinity = map->mp_affinity.base;
+ affinity |= (cluster_idx & 0xff) << map->mp_affinity.cluster_shift;
+ affinity |= (core_idx & 0xff) << map->mp_affinity.core_shift;
+
+ start_off = map->start_powered_off == SPO_ALL
+ || ((map->start_powered_off == SPO_SECONDARIES)
+ && (cluster_idx || core_idx));
+
+ name = g_strdup_printf("%s[*]", map->name);
+ object_property_add_child(OBJECT(qemu_cluster), name, obj);
+ object_property_set_bool(obj, "start-powered-off", start_off,
+ &error_abort);
+ qdev_prop_set_uint64(cpu, "mp-affinity", affinity);
+ qdev_prop_set_int32(cpu, "core-count", map->num_core);
+ object_property_set_link(obj, "memory", OBJECT(cpu_mr), &error_abort);
+ qdev_realize_and_unref(cpu, NULL, &error_fatal);
- object_property_set_int(obj, "mp-affinity", 0x100 | i, &error_abort);
- object_property_set_int(obj, "core-count", ARRAY_SIZE(s->lpd.rpu.cpu),
- &error_abort);
- object_property_set_link(obj, "memory", OBJECT(&s->lpd.rpu.mr),
- &error_abort);
- qdev_realize(DEVICE(obj), NULL, &error_fatal);
+ if (!map->dtb_expose) {
+ return cpu;
}
- qdev_realize(DEVICE(&s->lpd.rpu.cluster), NULL, &error_fatal);
+ node = versal_fdt_add_subnode(s, "/cpus/cpu", idx,
+ arm_cpu->dtb_compatible,
+ strlen(arm_cpu->dtb_compatible) + 1);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "reg",
+ arm_cpu_mp_affinity(arm_cpu) & ARM64_AFFINITY_MASK);
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "device_type", "cpu");
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "enable-method", "psci");
+
+ return cpu;
}
-static void versal_create_uarts(Versal *s, qemu_irq *pic)
+static void versal_create_cpu_cluster(Versal *s, const VersalCpuClusterMap *map)
{
- int i;
+ size_t i, j;
+ DeviceState *cluster;
+ MemoryRegion *mr;
+ char *name;
+ g_autofree DeviceState **cpus;
+ const char compatible[] = "arm,armv8-timer";
+ bool has_gtimer;
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) {
- static const int irqs[] = { VERSAL_UART0_IRQ_0, VERSAL_UART1_IRQ_0};
- static const uint64_t addrs[] = { MM_UART0, MM_UART1 };
- char *name = g_strdup_printf("uart%d", i);
- DeviceState *dev;
- MemoryRegion *mr;
+ cluster = qdev_new(TYPE_CPU_CLUSTER);
+ name = g_strdup_printf("%s-cluster", map->name);
+ object_property_add_child(OBJECT(s), name, OBJECT(cluster));
+ g_free(name);
+ qdev_prop_set_uint32(cluster, "cluster-id", map->qemu_cluster_id);
- object_initialize_child(OBJECT(s), name, &s->lpd.iou.uart[i],
- TYPE_PL011);
- dev = DEVICE(&s->lpd.iou.uart[i]);
- qdev_prop_set_chr(dev, "chardev", serial_hd(i));
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
+ mr = create_cpu_mr(s, cluster, map);
- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
- memory_region_add_subregion(&s->mr_ps, addrs[i], mr);
+ cpus = g_new(DeviceState *, map->num_cluster * map->num_core);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irqs[i]]);
- g_free(name);
+ if (map->dtb_expose) {
+ qemu_fdt_add_subnode(s->cfg.fdt, "/cpus");
+ qemu_fdt_setprop_cell(s->cfg.fdt, "/cpus", "#size-cells", 0);
+ qemu_fdt_setprop_cell(s->cfg.fdt, "/cpus", "#address-cells", 1);
+ }
+
+ for (i = 0; i < map->num_cluster; i++) {
+ for (j = 0; j < map->num_core; j++) {
+ DeviceState *cpu = versal_create_cpu(s, map, cluster, mr, i, j);
+
+ cpus[i * map->num_core + j] = cpu;
+ }
+
+ if (map->per_cluster_gic) {
+ versal_create_and_connect_gic(s, map, mr, &cpus[i * map->num_core],
+ map->num_core);
+ }
+ }
+
+ qdev_realize_and_unref(cluster, NULL, &error_fatal);
+
+ if (!map->per_cluster_gic) {
+ versal_create_and_connect_gic(s, map, mr, cpus,
+ map->num_cluster * map->num_core);
+ }
+
+ has_gtimer = arm_feature(&ARM_CPU(cpus[0])->env, ARM_FEATURE_GENERIC_TIMER);
+ if (map->dtb_expose && has_gtimer) {
+ qemu_fdt_add_subnode(s->cfg.fdt, "/timer");
+ qemu_fdt_setprop_cells(s->cfg.fdt, "/timer", "interrupts",
+ GIC_FDT_IRQ_TYPE_PPI,
+ INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ),
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI,
+ GIC_FDT_IRQ_TYPE_PPI,
+ INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ),
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI,
+ GIC_FDT_IRQ_TYPE_PPI,
+ INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ),
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI,
+ GIC_FDT_IRQ_TYPE_PPI,
+ INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ),
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop(s->cfg.fdt, "/timer", "compatible",
+ compatible, sizeof(compatible));
}
}
-static void versal_create_canfds(Versal *s, qemu_irq *pic)
+static void versal_create_uart(Versal *s,
+ const VersalSimplePeriphMap *map,
+ int chardev_idx)
{
- int i;
- uint32_t irqs[] = { VERSAL_CANFD0_IRQ_0, VERSAL_CANFD1_IRQ_0};
- uint64_t addrs[] = { MM_CANFD0, MM_CANFD1 };
+ DeviceState *dev;
+ MemoryRegion *mr;
+ g_autofree char *node;
+ g_autofree char *alias;
+ const char compatible[] = "arm,pl011\0arm,sbsa-uart";
+ const char clocknames[] = "uartclk\0apb_pclk";
+
+ dev = qdev_new(TYPE_PL011);
+ object_property_add_child(OBJECT(s), "uart[*]", OBJECT(dev));
+ qdev_prop_set_chr(dev, "chardev", serial_hd(chardev_idx));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.canfd); i++) {
- char *name = g_strdup_printf("canfd%d", i);
- SysBusDevice *sbd;
- MemoryRegion *mr;
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_add_subregion(&s->mr_ps, map->addr, mr);
+
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq);
+
+ node = versal_fdt_add_simple_subnode(s, "/uart", map->addr, 0x1000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "current-speed", 115200);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_125mhz, s->phandle.clk_125mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", clocknames,
+ sizeof(clocknames));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, map->irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop(s->cfg.fdt, node, "u-boot,dm-pre-reloc", NULL, 0);
+
+ alias = g_strdup_printf("serial%d", chardev_idx);
+ qemu_fdt_setprop_string(s->cfg.fdt, "/aliases", alias, node);
+
+ if (chardev_idx == 0) {
+ qemu_fdt_setprop_string(s->cfg.fdt, "/chosen", "stdout-path", node);
+ }
+}
- object_initialize_child(OBJECT(s), name, &s->lpd.iou.canfd[i],
- TYPE_XILINX_CANFD);
- sbd = SYS_BUS_DEVICE(&s->lpd.iou.canfd[i]);
+static void versal_create_canfd(Versal *s, const VersalSimplePeriphMap *map,
+ CanBusState *bus)
+{
+ SysBusDevice *sbd;
+ MemoryRegion *mr;
+ g_autofree char *node;
+ const char compatible[] = "xlnx,canfd-2.0";
+ const char clocknames[] = "can_clk\0s_axi_aclk";
- object_property_set_int(OBJECT(&s->lpd.iou.canfd[i]), "ext_clk_freq",
- XLNX_VERSAL_CANFD_REF_CLK , &error_abort);
+ sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XILINX_CANFD));
+ object_property_add_child(OBJECT(s), "canfd[*]", OBJECT(sbd));
- object_property_set_link(OBJECT(&s->lpd.iou.canfd[i]), "canfdbus",
- OBJECT(s->lpd.iou.canbus[i]),
- &error_abort);
+ object_property_set_int(OBJECT(sbd), "ext_clk_freq",
+ 25 * 1000 * 1000 , &error_abort);
- sysbus_realize(sbd, &error_fatal);
+ object_property_set_link(OBJECT(sbd), "canfdbus", OBJECT(bus),
+ &error_abort);
- mr = sysbus_mmio_get_region(sbd, 0);
- memory_region_add_subregion(&s->mr_ps, addrs[i], mr);
+ sysbus_realize_and_unref(sbd, &error_fatal);
- sysbus_connect_irq(sbd, 0, pic[irqs[i]]);
- g_free(name);
- }
+ mr = sysbus_mmio_get_region(sbd, 0);
+ memory_region_add_subregion(&s->mr_ps, map->addr, mr);
+
+ versal_sysbus_connect_irq(s, sbd, 0, map->irq);
+
+ node = versal_fdt_add_simple_subnode(s, "/canfd", map->addr, 0x10000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "rx-fifo-depth", 0x40);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "tx-mailbox-count", 0x20);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_25mhz, s->phandle.clk_25mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names",
+ clocknames, sizeof(clocknames));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, map->irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
}
-static void versal_create_usbs(Versal *s, qemu_irq *pic)
+static void versal_create_usb(Versal *s,
+ const struct VersalUsbMap *map)
{
DeviceState *dev;
MemoryRegion *mr;
+ g_autofree char *node, *subnode;
+ const char clocknames[] = "bus_clk\0ref_clk";
+ const char irq_name[] = "dwc_usb3";
+ const char compat_versal_dwc3[] = "xlnx,versal-dwc3";
+ const char compat_dwc3[] = "snps,dwc3";
- object_initialize_child(OBJECT(s), "usb2", &s->lpd.iou.usb,
- TYPE_XILINX_VERSAL_USB2);
- dev = DEVICE(&s->lpd.iou.usb);
+ dev = qdev_new(TYPE_XILINX_VERSAL_USB2);
+ object_property_add_child(OBJECT(s), "usb[*]", OBJECT(dev));
object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps),
&error_abort);
qdev_prop_set_uint32(dev, "intrs", 1);
qdev_prop_set_uint32(dev, "slots", 2);
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
- memory_region_add_subregion(&s->mr_ps, MM_USB_0, mr);
+ memory_region_add_subregion(&s->mr_ps, map->xhci, mr);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[VERSAL_USB0_IRQ_0]);
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
- memory_region_add_subregion(&s->mr_ps, MM_USB2_CTRL_REGS, mr);
+ memory_region_add_subregion(&s->mr_ps, map->ctrl, mr);
+
+ node = versal_fdt_add_simple_subnode(s, "/usb", map->ctrl, 0x10000,
+ compat_versal_dwc3,
+ sizeof(compat_versal_dwc3));
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names",
+ clocknames, sizeof(clocknames));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_25mhz, s->phandle.clk_125mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "ranges", NULL, 0);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "#address-cells", 2);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "#size-cells", 2);
+
+ subnode = g_strdup_printf("/%s/dwc3", node);
+ g_free(node);
+
+ node = versal_fdt_add_simple_subnode(s, subnode, map->xhci, 0x10000,
+ compat_dwc3,
+ sizeof(compat_dwc3));
+ qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-names",
+ irq_name, sizeof(irq_name));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, map->irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node,
+ "snps,quirk-frame-length-adjustment", 0x20);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "#stream-id-cells", 1);
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "dr_mode", "host");
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "phy-names", "usb3-phy");
+ qemu_fdt_setprop(s->cfg.fdt, node, "snps,dis_u2_susphy_quirk", NULL, 0);
+ qemu_fdt_setprop(s->cfg.fdt, node, "snps,dis_u3_susphy_quirk", NULL, 0);
+ qemu_fdt_setprop(s->cfg.fdt, node, "snps,refclk_fladj", NULL, 0);
+ qemu_fdt_setprop(s->cfg.fdt, node, "snps,mask_phy_reset", NULL, 0);
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "maximum-speed", "high-speed");
}
-static void versal_create_gems(Versal *s, qemu_irq *pic)
+static void versal_create_gem(Versal *s,
+ const struct VersalGemMap *map)
{
+ DeviceState *dev;
+ MemoryRegion *mr;
+ DeviceState *or;
int i;
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) {
- static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0};
- static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 };
- char *name = g_strdup_printf("gem%d", i);
- DeviceState *dev;
- MemoryRegion *mr;
- OrIRQState *or_irq;
-
- object_initialize_child(OBJECT(s), name, &s->lpd.iou.gem[i],
- TYPE_CADENCE_GEM);
- or_irq = &s->lpd.iou.gem_irq_orgate[i];
- object_initialize_child(OBJECT(s), "gem-irq-orgate[*]",
- or_irq, TYPE_OR_IRQ);
- dev = DEVICE(&s->lpd.iou.gem[i]);
- qemu_configure_nic_device(dev, true, NULL);
- object_property_set_int(OBJECT(dev), "phy-addr", 23, &error_abort);
- object_property_set_int(OBJECT(dev), "num-priority-queues", 2,
- &error_abort);
- object_property_set_int(OBJECT(or_irq),
- "num-lines", 2, &error_fatal);
- qdev_realize(DEVICE(or_irq), NULL, &error_fatal);
- qdev_connect_gpio_out(DEVICE(or_irq), 0, pic[irqs[i]]);
-
- object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps),
- &error_abort);
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
+ dev = qdev_new(TYPE_CADENCE_GEM);
+ object_property_add_child(OBJECT(s), "gem[*]", OBJECT(dev));
- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
- memory_region_add_subregion(&s->mr_ps, addrs[i], mr);
+ qemu_configure_nic_device(dev, true, NULL);
+ object_property_set_int(OBJECT(dev), "phy-addr", 23, &error_abort);
+ object_property_set_int(OBJECT(dev), "num-priority-queues",
+ map->num_prio_queue, &error_abort);
+
+ object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps),
+ &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_add_subregion(&s->mr_ps, map->map.addr, mr);
+
+ /*
+ * The GEM controller exposes one IRQ line per priority queue. In Versal
+ * family devices, those are OR'ed together.
+ */
+ or = create_or_gate(s, OBJECT(dev), "irq-orgate",
+ map->num_prio_queue, map->map.irq);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(or_irq), 0));
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(or_irq), 1));
- g_free(name);
+ for (i = 0; i < map->num_prio_queue; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(or, i));
}
}
-static void versal_create_admas(Versal *s, qemu_irq *pic)
+static void versal_create_gem_fdt(Versal *s,
+ const struct VersalGemMap *map)
{
int i;
-
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) {
- char *name = g_strdup_printf("adma%d", i);
- DeviceState *dev;
- MemoryRegion *mr;
-
- object_initialize_child(OBJECT(s), name, &s->lpd.iou.adma[i],
- TYPE_XLNX_ZDMA);
- dev = DEVICE(&s->lpd.iou.adma[i]);
- object_property_set_int(OBJECT(dev), "bus-width", 128, &error_abort);
- object_property_set_link(OBJECT(dev), "dma",
- OBJECT(get_system_memory()), &error_fatal);
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
-
- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
- memory_region_add_subregion(&s->mr_ps,
- MM_ADMA_CH0 + i * MM_ADMA_CH0_SIZE, mr);
-
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[VERSAL_ADMA_IRQ_0 + i]);
- g_free(name);
+ g_autofree char *node;
+ g_autofree char *phy_node;
+ int phy_phandle;
+ const char compatible[] = "cdns,zynqmp-gem\0cdns,gem";
+ const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk";
+ g_autofree uint32_t *irq_prop;
+
+ node = versal_fdt_add_simple_subnode(s, "/ethernet", map->map.addr, 0x1000,
+ compatible, sizeof(compatible));
+ phy_node = g_strdup_printf("%s/fixed-link", node);
+ phy_phandle = qemu_fdt_alloc_phandle(s->cfg.fdt);
+
+ /* Fixed link PHY node */
+ qemu_fdt_add_subnode(s->cfg.fdt, phy_node);
+ qemu_fdt_setprop_cell(s->cfg.fdt, phy_node, "phandle", phy_phandle);
+ qemu_fdt_setprop(s->cfg.fdt, phy_node, "full-duplex", NULL, 0);
+ qemu_fdt_setprop_cell(s->cfg.fdt, phy_node, "speed", map->speed);
+
+ qemu_fdt_setprop_string(s->cfg.fdt, node, "phy-mode", map->phy_mode);
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "phy-handle", phy_phandle);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_25mhz, s->phandle.clk_25mhz,
+ s->phandle.clk_125mhz, s->phandle.clk_125mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names",
+ clocknames, sizeof(clocknames));
+
+ irq_prop = g_new(uint32_t, map->num_prio_queue * 3);
+ for (i = 0; i < map->num_prio_queue; i++) {
+ irq_prop[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
+ irq_prop[3 * i + 1] = cpu_to_be32(map->map.irq);
+ irq_prop[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
}
+ qemu_fdt_setprop(s->cfg.fdt, node, "interrupts", irq_prop,
+ sizeof(uint32_t) * map->num_prio_queue * 3);
}
-#define SDHCI_CAPABILITIES 0x280737ec6481 /* Same as on ZynqMP. */
-static void versal_create_sds(Versal *s, qemu_irq *pic)
+static void versal_create_zdma(Versal *s,
+ const struct VersalZDMAMap *map)
{
- int i;
+ DeviceState *dev;
+ MemoryRegion *mr;
+ g_autofree char *name;
+ const char compatible[] = "xlnx,zynqmp-dma-1.0";
+ const char clocknames[] = "clk_main\0clk_apb";
+ size_t i;
- for (i = 0; i < ARRAY_SIZE(s->pmc.iou.sd); i++) {
- DeviceState *dev;
- MemoryRegion *mr;
+ name = g_strdup_printf("%s[*]", map->name);
- object_initialize_child(OBJECT(s), "sd[*]", &s->pmc.iou.sd[i],
- TYPE_SYSBUS_SDHCI);
- dev = DEVICE(&s->pmc.iou.sd[i]);
+ for (i = 0; i < map->num_chan; i++) {
+ uint64_t addr = map->map.addr + map->chan_stride * i;
+ int irq = map->map.irq + map->irq_stride * i;
+ g_autofree char *node;
- object_property_set_uint(OBJECT(dev), "sd-spec-version", 3,
- &error_fatal);
- object_property_set_uint(OBJECT(dev), "capareg", SDHCI_CAPABILITIES,
- &error_fatal);
- object_property_set_uint(OBJECT(dev), "uhs", UHS_I, &error_fatal);
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
+ dev = qdev_new(TYPE_XLNX_ZDMA);
+ object_property_add_child(OBJECT(s), name, OBJECT(dev));
+ object_property_set_int(OBJECT(dev), "bus-width", 128, &error_abort);
+ object_property_set_link(OBJECT(dev), "dma",
+ OBJECT(get_system_memory()), &error_fatal);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
- memory_region_add_subregion(&s->mr_ps,
- MM_PMC_SD0 + i * MM_PMC_SD0_SIZE, mr);
-
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
- pic[VERSAL_SD0_IRQ_0 + i * 2]);
+ memory_region_add_subregion(&s->mr_ps, addr, mr);
+
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, irq);
+
+ node = versal_fdt_add_simple_subnode(s, "/dma", addr, 0x1000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop_cell(s->cfg.fdt, node, "xlnx,bus-width", 64);
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_25mhz, s->phandle.clk_25mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names",
+ clocknames, sizeof(clocknames));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
}
}
-static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic)
+#define SDHCI_CAPABILITIES 0x280737ec6481 /* Same as on ZynqMP. */
+static void versal_create_sdhci(Versal *s,
+ const VersalSimplePeriphMap *map)
{
- DeviceState *orgate;
+ DeviceState *dev;
+ MemoryRegion *mr;
+ g_autofree char *node;
+ const char compatible[] = "arasan,sdhci-8.9a";
+ const char clocknames[] = "clk_xin\0clk_ahb";
+
+ dev = qdev_new(TYPE_SYSBUS_SDHCI);
+ object_property_add_child(OBJECT(s), "sdhci[*]", OBJECT(dev));
+
+ object_property_set_uint(OBJECT(dev), "sd-spec-version", 3,
+ &error_fatal);
+ object_property_set_uint(OBJECT(dev), "capareg", SDHCI_CAPABILITIES,
+ &error_fatal);
+ object_property_set_uint(OBJECT(dev), "uhs", UHS_I, &error_fatal);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- /*
- * The VERSAL_PMC_APB_IRQ is an 'or' of the interrupts from the following
- * models:
- * - RTC
- * - BBRAM
- * - PMC SLCR
- * - CFRAME regs (input 3 - 17 to the orgate)
- */
- object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate",
- &s->pmc.apb_irq_orgate, TYPE_OR_IRQ);
- orgate = DEVICE(&s->pmc.apb_irq_orgate);
- object_property_set_int(OBJECT(orgate),
- "num-lines", VERSAL_NUM_PMC_APB_IRQS, &error_fatal);
- qdev_realize(orgate, NULL, &error_fatal);
- qdev_connect_gpio_out(orgate, 0, pic[VERSAL_PMC_APB_IRQ]);
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_add_subregion(&s->mr_ps, map->addr, mr);
+
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq);
+
+ node = versal_fdt_add_simple_subnode(s, "/sdhci", map->addr, 0x10000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks",
+ s->phandle.clk_25mhz, s->phandle.clk_25mhz);
+ qemu_fdt_setprop(s->cfg.fdt, node, "clock-names",
+ clocknames, sizeof(clocknames));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, map->irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
}
-static void versal_create_rtc(Versal *s, qemu_irq *pic)
+static void versal_create_rtc(Versal *s, const struct VersalRtcMap *map)
{
SysBusDevice *sbd;
MemoryRegion *mr;
+ g_autofree char *node;
+ const char compatible[] = "xlnx,zynqmp-rtc";
+ const char interrupt_names[] = "alarm\0sec";
- object_initialize_child(OBJECT(s), "rtc", &s->pmc.rtc,
- TYPE_XLNX_ZYNQMP_RTC);
- sbd = SYS_BUS_DEVICE(&s->pmc.rtc);
- sysbus_realize(sbd, &error_fatal);
+ sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_ZYNQMP_RTC));
+ object_property_add_child(OBJECT(s), "rtc", OBJECT(sbd));
+ sysbus_realize_and_unref(sbd, &error_abort);
mr = sysbus_mmio_get_region(sbd, 0);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_RTC, mr);
+ memory_region_add_subregion(&s->mr_ps, map->map.addr, mr);
/*
* TODO: Connect the ALARM and SECONDS interrupts once our RTC model
* supports them.
*/
- sysbus_connect_irq(sbd, 1,
- qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0));
+ versal_sysbus_connect_irq(s, sbd, 0, map->map.irq);
+
+ node = versal_fdt_add_simple_subnode(s, "/rtc", map->map.addr, 0x10000,
+ compatible, sizeof(compatible));
+ qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, map->alarm_irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI,
+ GIC_FDT_IRQ_TYPE_SPI, map->second_irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-names",
+ interrupt_names, sizeof(interrupt_names));
}
-static void versal_create_trng(Versal *s, qemu_irq *pic)
+static void versal_create_trng(Versal *s, const VersalSimplePeriphMap *map)
{
SysBusDevice *sbd;
MemoryRegion *mr;
- object_initialize_child(OBJECT(s), "trng", &s->pmc.trng,
- TYPE_XLNX_VERSAL_TRNG);
- sbd = SYS_BUS_DEVICE(&s->pmc.trng);
- sysbus_realize(sbd, &error_fatal);
+ sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_VERSAL_TRNG));
+ object_property_add_child(OBJECT(s), "trng", OBJECT(sbd));
+ sysbus_realize_and_unref(sbd, &error_abort);
mr = sysbus_mmio_get_region(sbd, 0);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_TRNG, mr);
- sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]);
+ memory_region_add_subregion(&s->mr_ps, map->addr, mr);
+ versal_sysbus_connect_irq(s, sbd, 0, map->irq);
}
-static void versal_create_xrams(Versal *s, qemu_irq *pic)
+static void versal_create_xrams(Versal *s, const struct VersalXramMap *map)
{
- int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl);
- DeviceState *orgate;
- int i;
+ SysBusDevice *sbd;
+ MemoryRegion *mr;
+ DeviceState *or;
+ size_t i;
+
+ or = create_or_gate(s, OBJECT(s), "xram-orgate", map->num, map->irq);
- /* XRAM IRQs get ORed into a single line. */
- object_initialize_child(OBJECT(s), "xram-irq-orgate",
- &s->lpd.xram.irq_orgate, TYPE_OR_IRQ);
- orgate = DEVICE(&s->lpd.xram.irq_orgate);
- object_property_set_int(OBJECT(orgate),
- "num-lines", nr_xrams, &error_fatal);
- qdev_realize(orgate, NULL, &error_fatal);
- qdev_connect_gpio_out(orgate, 0, pic[VERSAL_XRAM_IRQ_0]);
+ for (i = 0; i < map->num; i++) {
+ hwaddr ctrl, mem;
- for (i = 0; i < ARRAY_SIZE(s->lpd.xram.ctrl); i++) {
- SysBusDevice *sbd;
- MemoryRegion *mr;
+ sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_XRAM_CTRL));
+ object_property_add_child(OBJECT(s), "xram[*]", OBJECT(sbd));
+ sysbus_realize_and_unref(sbd, &error_fatal);
- object_initialize_child(OBJECT(s), "xram[*]", &s->lpd.xram.ctrl[i],
- TYPE_XLNX_XRAM_CTRL);
- sbd = SYS_BUS_DEVICE(&s->lpd.xram.ctrl[i]);
- sysbus_realize(sbd, &error_fatal);
+ ctrl = map->ctrl + map->ctrl_stride * i;
+ mem = map->mem + map->mem_stride * i;
mr = sysbus_mmio_get_region(sbd, 0);
- memory_region_add_subregion(&s->mr_ps,
- MM_XRAMC + i * MM_XRAMC_SIZE, mr);
+ memory_region_add_subregion(&s->mr_ps, ctrl, mr);
mr = sysbus_mmio_get_region(sbd, 1);
- memory_region_add_subregion(&s->mr_ps, MM_XRAM + i * MiB, mr);
+ memory_region_add_subregion(&s->mr_ps, mem, mr);
- sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(orgate, i));
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(or, i));
}
}
-static void versal_create_bbram(Versal *s, qemu_irq *pic)
+static void versal_create_bbram(Versal *s,
+ const VersalSimplePeriphMap *map)
{
+ DeviceState *dev;
SysBusDevice *sbd;
- object_initialize_child_with_props(OBJECT(s), "bbram", &s->pmc.bbram,
- sizeof(s->pmc.bbram), TYPE_XLNX_BBRAM,
- &error_fatal,
- "crc-zpads", "0",
- NULL);
- sbd = SYS_BUS_DEVICE(&s->pmc.bbram);
+ dev = qdev_new(TYPE_XLNX_BBRAM);
+ sbd = SYS_BUS_DEVICE(dev);
- sysbus_realize(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_BBRAM_CTRL,
+ object_property_add_child(OBJECT(s), "bbram", OBJECT(dev));
+ qdev_prop_set_uint32(dev, "crc-zpads", 0);
+ sysbus_realize_and_unref(sbd, &error_abort);
+ memory_region_add_subregion(&s->mr_ps, map->addr,
sysbus_mmio_get_region(sbd, 0));
- sysbus_connect_irq(sbd, 0,
- qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 1));
+ versal_sysbus_connect_irq(s, sbd, 0, map->irq);
}
-static void versal_realize_efuse_part(Versal *s, Object *dev, hwaddr base)
+static void versal_create_efuse(Versal *s,
+ const struct VersalEfuseMap *map)
{
- SysBusDevice *part = SYS_BUS_DEVICE(dev);
+ DeviceState *bits;
+ DeviceState *ctrl;
+ DeviceState *cache;
- object_property_set_link(OBJECT(part), "efuse",
- OBJECT(&s->pmc.efuse), &error_abort);
+ if (versal_get_version(s) != VERSAL_VER_VERSAL) {
+ /* TODO for versal2 */
+ return;
+ }
- sysbus_realize(part, &error_abort);
- memory_region_add_subregion(&s->mr_ps, base,
- sysbus_mmio_get_region(part, 0));
-}
+ ctrl = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CTRL);
+ cache = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CACHE);
+ bits = qdev_new(TYPE_XLNX_EFUSE);
-static void versal_create_efuse(Versal *s, qemu_irq *pic)
-{
- Object *bits = OBJECT(&s->pmc.efuse);
- Object *ctrl = OBJECT(&s->pmc.efuse_ctrl);
- Object *cache = OBJECT(&s->pmc.efuse_cache);
+ qdev_prop_set_uint32(bits, "efuse-nr", 3);
+ qdev_prop_set_uint32(bits, "efuse-size", 8192);
+
+ object_property_add_child(OBJECT(s), "efuse", OBJECT(bits));
+ qdev_realize_and_unref(bits, NULL, &error_abort);
- object_initialize_child(OBJECT(s), "efuse-ctrl", &s->pmc.efuse_ctrl,
- TYPE_XLNX_VERSAL_EFUSE_CTRL);
+ object_property_set_link(OBJECT(ctrl), "efuse", OBJECT(bits), &error_abort);
- object_initialize_child(OBJECT(s), "efuse-cache", &s->pmc.efuse_cache,
- TYPE_XLNX_VERSAL_EFUSE_CACHE);
+ object_property_set_link(OBJECT(cache), "efuse", OBJECT(bits),
+ &error_abort);
- object_initialize_child_with_props(ctrl, "xlnx-efuse@0", bits,
- sizeof(s->pmc.efuse),
- TYPE_XLNX_EFUSE, &error_abort,
- "efuse-nr", "3",
- "efuse-size", "8192",
- NULL);
+ object_property_add_child(OBJECT(s), "efuse-cache", OBJECT(cache));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(cache), &error_abort);
- qdev_realize(DEVICE(bits), NULL, &error_abort);
- versal_realize_efuse_part(s, ctrl, MM_PMC_EFUSE_CTRL);
- versal_realize_efuse_part(s, cache, MM_PMC_EFUSE_CACHE);
+ object_property_add_child(OBJECT(s), "efuse-ctrl", OBJECT(ctrl));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ctrl), &error_abort);
- sysbus_connect_irq(SYS_BUS_DEVICE(ctrl), 0, pic[VERSAL_EFUSE_IRQ]);
+ memory_region_add_subregion(&s->mr_ps, map->ctrl,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(ctrl),
+ 0));
+ memory_region_add_subregion(&s->mr_ps, map->cache,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(cache),
+ 0));
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(ctrl), 0, map->irq);
}
-static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic)
+static DeviceState *versal_create_pmc_iou_slcr(Versal *s,
+ const VersalSimplePeriphMap *map)
{
SysBusDevice *sbd;
+ DeviceState *dev;
- object_initialize_child(OBJECT(s), "versal-pmc-iou-slcr", &s->pmc.iou.slcr,
- TYPE_XILINX_VERSAL_PMC_IOU_SLCR);
+ dev = qdev_new(TYPE_XILINX_VERSAL_PMC_IOU_SLCR);
+ object_property_add_child(OBJECT(s), "pmc-iou-slcr", OBJECT(dev));
- sbd = SYS_BUS_DEVICE(&s->pmc.iou.slcr);
- sysbus_realize(sbd, &error_fatal);
+ sbd = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_PMC_IOU_SLCR,
+ memory_region_add_subregion(&s->mr_ps, map->addr,
sysbus_mmio_get_region(sbd, 0));
- sysbus_connect_irq(sbd, 0,
- qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2));
+ versal_sysbus_connect_irq(s, sbd, 0, map->irq);
+
+ return dev;
}
-static void versal_create_ospi(Versal *s, qemu_irq *pic)
+static DeviceState *versal_create_ospi(Versal *s,
+ const struct VersalOspiMap *map)
{
SysBusDevice *sbd;
MemoryRegion *mr_dac;
- qemu_irq ospi_mux_sel;
- DeviceState *orgate;
+ DeviceState *dev, *dma_dst, *dma_src, *orgate;
+ MemoryRegion *linear_mr = g_new(MemoryRegion, 1);
- memory_region_init(&s->pmc.iou.ospi.linear_mr, OBJECT(s),
- "versal-ospi-linear-mr" , MM_PMC_OSPI_DAC_SIZE);
+ dev = qdev_new(TYPE_XILINX_VERSAL_OSPI);
+ object_property_add_child(OBJECT(s), "ospi", OBJECT(dev));
- object_initialize_child(OBJECT(s), "versal-ospi", &s->pmc.iou.ospi.ospi,
- TYPE_XILINX_VERSAL_OSPI);
+ memory_region_init(linear_mr, OBJECT(dev), "linear-mr", map->dac_sz);
- mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 1);
- memory_region_add_subregion(&s->pmc.iou.ospi.linear_mr, 0x0, mr_dac);
+ mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+ memory_region_add_subregion(linear_mr, 0x0, mr_dac);
/* Create the OSPI destination DMA */
- object_initialize_child(OBJECT(s), "versal-ospi-dma-dst",
- &s->pmc.iou.ospi.dma_dst,
- TYPE_XLNX_CSU_DMA);
+ dma_dst = qdev_new(TYPE_XLNX_CSU_DMA);
+ object_property_add_child(OBJECT(dev), "dma-dst-dev", OBJECT(dma_dst));
+ object_property_set_link(OBJECT(dma_dst), "dma",
+ OBJECT(get_system_memory()), &error_abort);
- object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_dst),
- "dma", OBJECT(get_system_memory()),
- &error_abort);
+ sbd = SYS_BUS_DEVICE(dma_dst);
+ sysbus_realize_and_unref(sbd, &error_fatal);
- sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst);
- sysbus_realize(sbd, &error_fatal);
-
- memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_DST,
+ memory_region_add_subregion(&s->mr_ps, map->dma_dst,
sysbus_mmio_get_region(sbd, 0));
/* Create the OSPI source DMA */
- object_initialize_child(OBJECT(s), "versal-ospi-dma-src",
- &s->pmc.iou.ospi.dma_src,
- TYPE_XLNX_CSU_DMA);
-
- object_property_set_bool(OBJECT(&s->pmc.iou.ospi.dma_src), "is-dst",
- false, &error_abort);
+ dma_src = qdev_new(TYPE_XLNX_CSU_DMA);
+ object_property_add_child(OBJECT(dev), "dma-src-dev", OBJECT(dma_src));
- object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src),
- "dma", OBJECT(mr_dac), &error_abort);
+ object_property_set_bool(OBJECT(dma_src), "is-dst", false, &error_abort);
- object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src),
- "stream-connected-dma",
- OBJECT(&s->pmc.iou.ospi.dma_dst),
+ object_property_set_link(OBJECT(dma_src), "dma", OBJECT(mr_dac),
&error_abort);
- sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src);
- sysbus_realize(sbd, &error_fatal);
+ object_property_set_link(OBJECT(dma_src), "stream-connected-dma",
+ OBJECT(dma_dst), &error_abort);
+
+ sbd = SYS_BUS_DEVICE(dma_src);
+ sysbus_realize_and_unref(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_SRC,
+ memory_region_add_subregion(&s->mr_ps, map->dma_src,
sysbus_mmio_get_region(sbd, 0));
/* Realize the OSPI */
- object_property_set_link(OBJECT(&s->pmc.iou.ospi.ospi), "dma-src",
- OBJECT(&s->pmc.iou.ospi.dma_src), &error_abort);
+ object_property_set_link(OBJECT(dev), "dma-src",
+ OBJECT(dma_src), &error_abort);
- sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi);
- sysbus_realize(sbd, &error_fatal);
+ sbd = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI,
+ memory_region_add_subregion(&s->mr_ps, map->ctrl,
sysbus_mmio_get_region(sbd, 0));
- memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DAC,
- &s->pmc.iou.ospi.linear_mr);
-
- /* ospi_mux_sel */
- ospi_mux_sel = qdev_get_gpio_in_named(DEVICE(&s->pmc.iou.ospi.ospi),
- "ospi-mux-sel", 0);
- qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "ospi-mux-sel", 0,
- ospi_mux_sel);
+ memory_region_add_subregion(&s->mr_ps, map->dac,
+ linear_mr);
/* OSPI irq */
- object_initialize_child(OBJECT(s), "ospi-irq-orgate",
- &s->pmc.iou.ospi.irq_orgate, TYPE_OR_IRQ);
- object_property_set_int(OBJECT(&s->pmc.iou.ospi.irq_orgate),
- "num-lines", NUM_OSPI_IRQ_LINES, &error_fatal);
-
- orgate = DEVICE(&s->pmc.iou.ospi.irq_orgate);
- qdev_realize(orgate, NULL, &error_fatal);
+ orgate = create_or_gate(s, OBJECT(dev), "irq-orgate", 3,
+ map->irq);
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 0,
- qdev_get_gpio_in(orgate, 0));
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src), 0,
- qdev_get_gpio_in(orgate, 1));
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst), 0,
- qdev_get_gpio_in(orgate, 2));
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(orgate, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(dma_src), 0, qdev_get_gpio_in(orgate, 1));
+ sysbus_connect_irq(SYS_BUS_DEVICE(dma_dst), 0, qdev_get_gpio_in(orgate, 2));
- qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]);
+ return dev;
}
-static void versal_create_cfu(Versal *s, qemu_irq *pic)
+static void versal_create_cfu(Versal *s, const struct VersalCfuMap *map)
{
SysBusDevice *sbd;
- DeviceState *dev;
+ Object *container;
+ DeviceState *cfu_fdro, *cfu_apb, *cfu_sfr, *cframe_bcast;
+ DeviceState *cframe_irq_or;
int i;
- const struct {
- uint64_t reg_base;
- uint64_t fdri_base;
- } cframe_addr[] = {
- { MM_PMC_CFRAME0_REG, MM_PMC_CFRAME0_FDRI },
- { MM_PMC_CFRAME1_REG, MM_PMC_CFRAME1_FDRI },
- { MM_PMC_CFRAME2_REG, MM_PMC_CFRAME2_FDRI },
- { MM_PMC_CFRAME3_REG, MM_PMC_CFRAME3_FDRI },
- { MM_PMC_CFRAME4_REG, MM_PMC_CFRAME4_FDRI },
- { MM_PMC_CFRAME5_REG, MM_PMC_CFRAME5_FDRI },
- { MM_PMC_CFRAME6_REG, MM_PMC_CFRAME6_FDRI },
- { MM_PMC_CFRAME7_REG, MM_PMC_CFRAME7_FDRI },
- { MM_PMC_CFRAME8_REG, MM_PMC_CFRAME8_FDRI },
- { MM_PMC_CFRAME9_REG, MM_PMC_CFRAME9_FDRI },
- { MM_PMC_CFRAME10_REG, MM_PMC_CFRAME10_FDRI },
- { MM_PMC_CFRAME11_REG, MM_PMC_CFRAME11_FDRI },
- { MM_PMC_CFRAME12_REG, MM_PMC_CFRAME12_FDRI },
- { MM_PMC_CFRAME13_REG, MM_PMC_CFRAME13_FDRI },
- { MM_PMC_CFRAME14_REG, MM_PMC_CFRAME14_FDRI },
- };
- const struct {
- uint32_t blktype0_frames;
- uint32_t blktype1_frames;
- uint32_t blktype2_frames;
- uint32_t blktype3_frames;
- uint32_t blktype4_frames;
- uint32_t blktype5_frames;
- uint32_t blktype6_frames;
- } cframe_cfg[] = {
- [0] = { 34111, 3528, 12800, 11, 5, 1, 1 },
- [1] = { 38498, 3841, 15361, 13, 7, 3, 1 },
- [2] = { 38498, 3841, 15361, 13, 7, 3, 1 },
- [3] = { 38498, 3841, 15361, 13, 7, 3, 1 },
- };
+
+ container = object_new(TYPE_CONTAINER);
+ object_property_add_child(OBJECT(s), "cfu", container);
+ object_unref(container);
/* CFU FDRO */
- object_initialize_child(OBJECT(s), "cfu-fdro", &s->pmc.cfu_fdro,
- TYPE_XLNX_VERSAL_CFU_FDRO);
- sbd = SYS_BUS_DEVICE(&s->pmc.cfu_fdro);
+ cfu_fdro = qdev_new(TYPE_XLNX_VERSAL_CFU_FDRO);
+ object_property_add_child(container, "cfu-fdro", OBJECT(cfu_fdro));
+ sbd = SYS_BUS_DEVICE(cfu_fdro);
- sysbus_realize(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_FDRO,
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ memory_region_add_subregion(&s->mr_ps, map->cfu_fdro,
sysbus_mmio_get_region(sbd, 0));
- /* CFRAME REG */
- for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) {
- g_autofree char *name = g_strdup_printf("cframe%d", i);
+ /* cframe bcast */
+ cframe_bcast = qdev_new(TYPE_XLNX_VERSAL_CFRAME_BCAST_REG);
+ object_property_add_child(container, "cframe-bcast", OBJECT(cframe_bcast));
- object_initialize_child(OBJECT(s), name, &s->pmc.cframe[i],
- TYPE_XLNX_VERSAL_CFRAME_REG);
+ /* CFU APB */
+ cfu_apb = qdev_new(TYPE_XLNX_VERSAL_CFU_APB);
+ object_property_add_child(container, "cfu-apb", OBJECT(cfu_apb));
+
+ /* IRQ or gate for cframes */
+ cframe_irq_or = qdev_new(TYPE_OR_IRQ);
+ object_property_add_child(container, "cframe-irq-or-gate",
+ OBJECT(cframe_irq_or));
+ qdev_prop_set_uint16(cframe_irq_or, "num-lines", map->num_cframe);
+ qdev_realize_and_unref(cframe_irq_or, NULL, &error_abort);
+ versal_qdev_connect_gpio_out(s, cframe_irq_or, 0, map->cframe_irq);
+
+ /* cframe reg */
+ for (i = 0; i < map->num_cframe; i++) {
+ uint64_t reg_base;
+ uint64_t fdri_base;
+ DeviceState *dev;
+ g_autofree char *prop_name;
+ size_t j;
- sbd = SYS_BUS_DEVICE(&s->pmc.cframe[i]);
- dev = DEVICE(&s->pmc.cframe[i]);
+ dev = qdev_new(TYPE_XLNX_VERSAL_CFRAME_REG);
+ object_property_add_child(container, "cframe[*]", OBJECT(dev));
- if (i < ARRAY_SIZE(cframe_cfg)) {
- object_property_set_int(OBJECT(dev), "blktype0-frames",
- cframe_cfg[i].blktype0_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype1-frames",
- cframe_cfg[i].blktype1_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype2-frames",
- cframe_cfg[i].blktype2_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype3-frames",
- cframe_cfg[i].blktype3_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype4-frames",
- cframe_cfg[i].blktype4_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype5-frames",
- cframe_cfg[i].blktype5_frames,
- &error_abort);
- object_property_set_int(OBJECT(dev), "blktype6-frames",
- cframe_cfg[i].blktype6_frames,
+ sbd = SYS_BUS_DEVICE(dev);
+
+ for (j = 0; j < ARRAY_SIZE(map->cframe_cfg[i].blktype_frames); j++) {
+ g_autofree char *blktype_prop_name;
+
+ blktype_prop_name = g_strdup_printf("blktype%zu-frames", j);
+ object_property_set_int(OBJECT(dev), blktype_prop_name,
+ map->cframe_cfg[i].blktype_frames[j],
&error_abort);
}
+
object_property_set_link(OBJECT(dev), "cfu-fdro",
- OBJECT(&s->pmc.cfu_fdro), &error_fatal);
+ OBJECT(cfu_fdro), &error_abort);
- sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort);
- memory_region_add_subregion(&s->mr_ps, cframe_addr[i].reg_base,
+ reg_base = map->cframe_base + i * map->cframe_stride * 2;
+ fdri_base = reg_base + map->cframe_stride;
+ memory_region_add_subregion(&s->mr_ps, reg_base,
sysbus_mmio_get_region(sbd, 0));
- memory_region_add_subregion(&s->mr_ps, cframe_addr[i].fdri_base,
+ memory_region_add_subregion(&s->mr_ps, fdri_base,
sysbus_mmio_get_region(sbd, 1));
- sysbus_connect_irq(sbd, 0,
- qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate),
- 3 + i));
- }
-
- /* CFRAME BCAST */
- object_initialize_child(OBJECT(s), "cframe_bcast", &s->pmc.cframe_bcast,
- TYPE_XLNX_VERSAL_CFRAME_BCAST_REG);
-
- sbd = SYS_BUS_DEVICE(&s->pmc.cframe_bcast);
- dev = DEVICE(&s->pmc.cframe_bcast);
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(cframe_irq_or, i));
- for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) {
- g_autofree char *propname = g_strdup_printf("cframe%d", i);
- object_property_set_link(OBJECT(dev), propname,
- OBJECT(&s->pmc.cframe[i]), &error_fatal);
+ prop_name = g_strdup_printf("cframe%d", i);
+ object_property_set_link(OBJECT(cframe_bcast), prop_name,
+ OBJECT(dev), &error_abort);
+ object_property_set_link(OBJECT(cfu_apb), prop_name,
+ OBJECT(dev), &error_abort);
}
- sysbus_realize(sbd, &error_fatal);
-
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_REG,
+ sbd = SYS_BUS_DEVICE(cframe_bcast);
+ sysbus_realize_and_unref(sbd, &error_abort);
+ memory_region_add_subregion(&s->mr_ps, map->cframe_bcast_reg,
sysbus_mmio_get_region(sbd, 0));
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_FDRI,
+ memory_region_add_subregion(&s->mr_ps, map->cframe_bcast_fdri,
sysbus_mmio_get_region(sbd, 1));
- /* CFU APB */
- object_initialize_child(OBJECT(s), "cfu-apb", &s->pmc.cfu_apb,
- TYPE_XLNX_VERSAL_CFU_APB);
- sbd = SYS_BUS_DEVICE(&s->pmc.cfu_apb);
- dev = DEVICE(&s->pmc.cfu_apb);
-
- for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) {
- g_autofree char *propname = g_strdup_printf("cframe%d", i);
- object_property_set_link(OBJECT(dev), propname,
- OBJECT(&s->pmc.cframe[i]), &error_fatal);
- }
-
- sysbus_realize(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_APB,
+ sbd = SYS_BUS_DEVICE(cfu_apb);
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ memory_region_add_subregion(&s->mr_ps, map->cfu_apb,
sysbus_mmio_get_region(sbd, 0));
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM,
+ memory_region_add_subregion(&s->mr_ps, map->cfu_stream,
sysbus_mmio_get_region(sbd, 1));
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM_2,
+ memory_region_add_subregion(&s->mr_ps, map->cfu_stream_2,
sysbus_mmio_get_region(sbd, 2));
- sysbus_connect_irq(sbd, 0, pic[VERSAL_CFU_IRQ_0]);
+ versal_sysbus_connect_irq(s, sbd, 0, map->cfu_apb_irq);
/* CFU SFR */
- object_initialize_child(OBJECT(s), "cfu-sfr", &s->pmc.cfu_sfr,
- TYPE_XLNX_VERSAL_CFU_SFR);
-
- sbd = SYS_BUS_DEVICE(&s->pmc.cfu_sfr);
-
- object_property_set_link(OBJECT(&s->pmc.cfu_sfr),
- "cfu", OBJECT(&s->pmc.cfu_apb), &error_abort);
-
- sysbus_realize(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_SFR,
+ cfu_sfr = qdev_new(TYPE_XLNX_VERSAL_CFU_SFR);
+ object_property_add_child(container, "cfu-sfr", OBJECT(cfu_sfr));
+ sbd = SYS_BUS_DEVICE(cfu_sfr);
+
+ object_property_set_link(OBJECT(cfu_sfr),
+ "cfu", OBJECT(cfu_apb), &error_abort);
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ memory_region_add_subregion(&s->mr_ps, map->cfu_sfr,
sysbus_mmio_get_region(sbd, 0));
}
-static void versal_create_crl(Versal *s, qemu_irq *pic)
+static inline void crl_connect_dev(Object *crl, Object *dev)
{
- SysBusDevice *sbd;
- int i;
+ const char *prop = object_get_canonical_path_component(dev);
- object_initialize_child(OBJECT(s), "crl", &s->lpd.crl,
- TYPE_XLNX_VERSAL_CRL);
- sbd = SYS_BUS_DEVICE(&s->lpd.crl);
+ /* The component part of the device path matches the CRL property name */
+ object_property_set_link(crl, prop, dev, &error_abort);
+}
- for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) {
- g_autofree gchar *name = g_strdup_printf("cpu_r5[%d]", i);
+static inline void crl_connect_dev_by_name(Versal *s, Object *crl,
+ const char *name, size_t num)
+{
+ size_t i;
- object_property_set_link(OBJECT(&s->lpd.crl),
- name, OBJECT(&s->lpd.rpu.cpu[i]),
- &error_abort);
+ for (i = 0; i < num; i++) {
+ Object *dev = versal_get_child_idx(s, name, i);
+
+ crl_connect_dev(crl, dev);
}
+}
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) {
- g_autofree gchar *name = g_strdup_printf("gem[%d]", i);
+static inline void versal_create_crl(Versal *s)
+{
+ const VersalMap *map;
+ VersalVersion ver;
+ const char *crl_class;
+ DeviceState *dev;
+ size_t num_gem;
+ Object *obj;
- object_property_set_link(OBJECT(&s->lpd.crl),
- name, OBJECT(&s->lpd.iou.gem[i]),
- &error_abort);
- }
+ map = versal_get_map(s);
+ ver = versal_get_version(s);
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) {
- g_autofree gchar *name = g_strdup_printf("adma[%d]", i);
+ crl_class = xlnx_versal_crl_class_name(ver);
+ dev = qdev_new(crl_class);
+ obj = OBJECT(dev);
+ object_property_add_child(OBJECT(s), "crl", obj);
- object_property_set_link(OBJECT(&s->lpd.crl),
- name, OBJECT(&s->lpd.iou.adma[i]),
- &error_abort);
- }
+ /*
+ * The 3rd GEM controller on versal2 is in the MMI subsystem.
+ * Its reset line is not connected to the CRL. Consider only the first two
+ * ones.
+ */
+ num_gem = ver == VERSAL_VER_VERSAL2 ? 2 : map->num_gem;
- for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) {
- g_autofree gchar *name = g_strdup_printf("uart[%d]", i);
+ crl_connect_dev_by_name(s, obj, "rpu-cluster/rpu",
+ map->rpu.num_cluster * map->rpu.num_core);
+ crl_connect_dev_by_name(s, obj, map->zdma[0].name, map->zdma[0].num_chan);
+ crl_connect_dev_by_name(s, obj, "uart", map->num_uart);
+ crl_connect_dev_by_name(s, obj, "gem", num_gem);
+ crl_connect_dev_by_name(s, obj, "usb", map->num_usb);
- object_property_set_link(OBJECT(&s->lpd.crl),
- name, OBJECT(&s->lpd.iou.uart[i]),
- &error_abort);
- }
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort);
- object_property_set_link(OBJECT(&s->lpd.crl),
- "usb", OBJECT(&s->lpd.iou.usb),
- &error_abort);
+ memory_region_add_subregion(&s->mr_ps, map->crl.addr,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
- sysbus_realize(sbd, &error_fatal);
- memory_region_add_subregion(&s->mr_ps, MM_CRL,
- sysbus_mmio_get_region(sbd, 0));
- sysbus_connect_irq(sbd, 0, pic[VERSAL_CRL_IRQ]);
+ if (ver == VERSAL_VER_VERSAL) {
+ /* CRL IRQ line has been removed in versal2 */
+ versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->crl.irq);
+ }
}
-/* This takes the board allocated linear DDR memory and creates aliases
+/*
+ * This takes the board allocated linear DDR memory and creates aliases
* for each split DDR range/aperture on the Versal address map.
*/
-static void versal_map_ddr(Versal *s)
+static void versal_map_ddr(Versal *s, const struct VersalDDRMap *map)
{
uint64_t size = memory_region_size(s->cfg.mr_ddr);
- /* Describes the various split DDR access regions. */
- static const struct {
- uint64_t base;
- uint64_t size;
- } addr_ranges[] = {
- { MM_TOP_DDR, MM_TOP_DDR_SIZE },
- { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE },
- { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE },
- { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE }
- };
uint64_t offset = 0;
int i;
- assert(ARRAY_SIZE(addr_ranges) == ARRAY_SIZE(s->noc.mr_ddr_ranges));
- for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) {
- char *name;
+ for (i = 0; i < map->num_chan && size; i++) {
uint64_t mapsize;
+ MemoryRegion *alias;
+
+ mapsize = MIN(size, map->chan[i].size);
- mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size;
- name = g_strdup_printf("noc-ddr-range%d", i);
/* Create the MR alias. */
- memory_region_init_alias(&s->noc.mr_ddr_ranges[i], OBJECT(s),
- name, s->cfg.mr_ddr,
- offset, mapsize);
+ alias = g_new(MemoryRegion, 1);
+ memory_region_init_alias(alias, OBJECT(s), "noc-ddr-range",
+ s->cfg.mr_ddr, offset, mapsize);
/* Map it onto the NoC MR. */
- memory_region_add_subregion(&s->mr_ps, addr_ranges[i].base,
- &s->noc.mr_ddr_ranges[i]);
+ memory_region_add_subregion(&s->mr_ps, map->chan[i].addr, alias);
offset += mapsize;
size -= mapsize;
- g_free(name);
}
}
+void versal_fdt_add_memory_nodes(Versal *s, uint64_t size)
+{
+ const struct VersalDDRMap *map = &versal_get_map(s)->ddr;
+ g_autofree char *node;
+ g_autofree uint64_t *reg;
+ int i;
+
+ reg = g_new(uint64_t, map->num_chan * 2);
+
+ for (i = 0; i < map->num_chan && size; i++) {
+ uint64_t mapsize;
+
+ mapsize = MIN(size, map->chan[i].size);
+
+ reg[i * 2] = cpu_to_be64(map->chan[i].addr);
+ reg[i * 2 + 1] = cpu_to_be64(mapsize);
+
+ size -= mapsize;
+ }
+
+ node = versal_fdt_add_subnode(s, "/memory", 0, "memory", sizeof("memory"));
+ qemu_fdt_setprop(s->cfg.fdt, node, "reg", reg, sizeof(uint64_t) * i * 2);
+}
+
static void versal_unimp_area(Versal *s, const char *name,
MemoryRegion *mr,
hwaddr base, hwaddr size)
@@ -875,22 +1794,12 @@ static void versal_unimp_irq_parity_imr(void *opaque, int n, int level)
"is not yet implemented\n");
}
-static void versal_unimp(Versal *s)
+static void versal_unimp_common(Versal *s)
{
+ DeviceState *slcr;
qemu_irq gpio_in;
- versal_unimp_area(s, "psm", &s->mr_ps,
- MM_PSM_START, MM_PSM_END - MM_PSM_START);
- versal_unimp_area(s, "crf", &s->mr_ps,
- MM_FPD_CRF, MM_FPD_CRF_SIZE);
- versal_unimp_area(s, "apu", &s->mr_ps,
- MM_FPD_FPD_APU, MM_FPD_FPD_APU_SIZE);
- versal_unimp_area(s, "crp", &s->mr_ps,
- MM_PMC_CRP, MM_PMC_CRP_SIZE);
- versal_unimp_area(s, "iou-scntr", &s->mr_ps,
- MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE);
- versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps,
- MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE);
+ versal_unimp_area(s, "crp", &s->mr_ps, 0xf1260000, 0x10000);
qdev_init_gpio_in_named(DEVICE(s), versal_unimp_sd_emmc_sel,
"sd-emmc-sel-dummy", 2);
@@ -899,102 +1808,353 @@ static void versal_unimp(Versal *s)
qdev_init_gpio_in_named(DEVICE(s), versal_unimp_irq_parity_imr,
"irq-parity-imr-dummy", 1);
+ slcr = DEVICE(versal_get_child(s, "pmc-iou-slcr"));
gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 0);
- qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 0,
- gpio_in);
+ qdev_connect_gpio_out_named(slcr, "sd-emmc-sel", 0, gpio_in);
gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 1);
- qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 1,
- gpio_in);
+ qdev_connect_gpio_out_named(slcr, "sd-emmc-sel", 1, gpio_in);
gpio_in = qdev_get_gpio_in_named(DEVICE(s), "qspi-ospi-mux-sel-dummy", 0);
- qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr),
- "qspi-ospi-mux-sel", 0,
- gpio_in);
+ qdev_connect_gpio_out_named(slcr, "qspi-ospi-mux-sel", 0, gpio_in);
gpio_in = qdev_get_gpio_in_named(DEVICE(s), "irq-parity-imr-dummy", 0);
- qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr),
- SYSBUS_DEVICE_GPIO_IRQ, 0,
- gpio_in);
+ qdev_connect_gpio_out_named(slcr, SYSBUS_DEVICE_GPIO_IRQ, 0, gpio_in);
+}
+
+static void versal_unimp(Versal *s)
+{
+ versal_unimp_area(s, "psm", &s->mr_ps, 0xffc80000, 0x70000);
+ versal_unimp_area(s, "crf", &s->mr_ps, 0xfd1a0000, 0x140000);
+ versal_unimp_area(s, "apu", &s->mr_ps, 0xfd5c0000, 0x100);
+ versal_unimp_area(s, "iou-scntr", &s->mr_ps, 0xff130000, 0x10000);
+ versal_unimp_area(s, "iou-scntr-secure", &s->mr_ps, 0xff140000, 0x10000);
+
+ versal_unimp_common(s);
+}
+
+static void versal2_unimp(Versal *s)
+{
+ versal_unimp_area(s, "fpd-systmr-ctrl", &s->mr_ps, 0xec920000, 0x1000);
+ versal_unimp_area(s, "crf", &s->mr_ps, 0xec200000, 0x100000);
+
+ versal_unimp_common(s);
+}
+
+static uint32_t fdt_add_clk_node(Versal *s, const char *name,
+ unsigned int freq_hz)
+{
+ uint32_t phandle;
+
+ phandle = qemu_fdt_alloc_phandle(s->cfg.fdt);
+
+ qemu_fdt_add_subnode(s->cfg.fdt, name);
+ qemu_fdt_setprop_cell(s->cfg.fdt, name, "phandle", phandle);
+ qemu_fdt_setprop_cell(s->cfg.fdt, name, "clock-frequency", freq_hz);
+ qemu_fdt_setprop_cell(s->cfg.fdt, name, "#clock-cells", 0x0);
+ qemu_fdt_setprop_string(s->cfg.fdt, name, "compatible", "fixed-clock");
+ qemu_fdt_setprop(s->cfg.fdt, name, "u-boot,dm-pre-reloc", NULL, 0);
+
+ return phandle;
+}
+
+static void versal_realize_common(Versal *s)
+{
+ DeviceState *slcr, *ospi;
+ MemoryRegion *ocm;
+ Object *container;
+ const VersalMap *map = versal_get_map(s);
+ size_t i;
+
+ g_assert(s->cfg.fdt != NULL);
+
+ s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000);
+ s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000);
+ s->phandle.gic = qemu_fdt_alloc_phandle(s->cfg.fdt);
+
+ container = object_new(TYPE_CONTAINER);
+ object_property_add_child(OBJECT(s), "irq-splits", container);
+ object_unref(container);
+
+ container = object_new(TYPE_CONTAINER);
+ object_property_add_child(OBJECT(s), "irq-or-gates", container);
+ object_unref(container);
+
+ qemu_fdt_setprop_cell(s->cfg.fdt, "/", "interrupt-parent", s->phandle.gic);
+ qemu_fdt_setprop_cell(s->cfg.fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(s->cfg.fdt, "/", "#address-cells", 0x2);
+
+ versal_create_cpu_cluster(s, &map->apu);
+ versal_create_cpu_cluster(s, &map->rpu);
+
+ for (i = 0; i < map->num_uart; i++) {
+ versal_create_uart(s, &map->uart[i], i);
+ }
+
+ for (i = 0; i < map->num_canfd; i++) {
+ versal_create_canfd(s, &map->canfd[i], s->cfg.canbus[i]);
+ }
+
+ for (i = 0; i < map->num_sdhci; i++) {
+ versal_create_sdhci(s, &map->sdhci[i]);
+ }
+
+ for (i = 0; i < map->num_gem; i++) {
+ versal_create_gem(s, &map->gem[i]);
+ /*
+ * Create fdt node in reverse order to keep backward compatibility with
+ * previous versions of the generated FDT. This affects Linux kernel
+ * interface naming order when persistent naming scheme is not in use.
+ */
+ versal_create_gem_fdt(s, &map->gem[map->num_gem - 1 - i]);
+ }
+
+ for (i = 0; i < map->num_zdma; i++) {
+ versal_create_zdma(s, &map->zdma[i]);
+ }
+
+ versal_create_xrams(s, &map->xram);
+
+ for (i = 0; i < map->num_usb; i++) {
+ versal_create_usb(s, &map->usb[i]);
+ }
+
+ versal_create_efuse(s, &map->efuse);
+ ospi = versal_create_ospi(s, &map->ospi);
+ slcr = versal_create_pmc_iou_slcr(s, &map->pmc_iou_slcr);
+
+ qdev_connect_gpio_out_named(slcr, "ospi-mux-sel", 0,
+ qdev_get_gpio_in_named(ospi,
+ "ospi-mux-sel", 0));
+
+ versal_create_bbram(s, &map->bbram);
+ versal_create_trng(s, &map->trng);
+ versal_create_rtc(s, &map->rtc);
+ versal_create_cfu(s, &map->cfu);
+ versal_create_crl(s);
+
+ versal_map_ddr(s, &map->ddr);
+
+ /* Create the On Chip Memory (OCM). */
+ ocm = g_new(MemoryRegion, 1);
+ memory_region_init_ram(ocm, OBJECT(s), "ocm", map->ocm.size, &error_fatal);
+ memory_region_add_subregion_overlap(&s->mr_ps, map->ocm.addr, ocm, 0);
}
static void versal_realize(DeviceState *dev, Error **errp)
{
- Versal *s = XLNX_VERSAL(dev);
- qemu_irq pic[XLNX_VERSAL_NR_IRQS];
-
- versal_create_apu_cpus(s);
- versal_create_apu_gic(s, pic);
- versal_create_rpu_cpus(s);
- versal_create_uarts(s, pic);
- versal_create_canfds(s, pic);
- versal_create_usbs(s, pic);
- versal_create_gems(s, pic);
- versal_create_admas(s, pic);
- versal_create_sds(s, pic);
- versal_create_pmc_apb_irq_orgate(s, pic);
- versal_create_rtc(s, pic);
- versal_create_trng(s, pic);
- versal_create_xrams(s, pic);
- versal_create_bbram(s, pic);
- versal_create_efuse(s, pic);
- versal_create_pmc_iou_slcr(s, pic);
- versal_create_ospi(s, pic);
- versal_create_crl(s, pic);
- versal_create_cfu(s, pic);
- versal_map_ddr(s);
+ Versal *s = XLNX_VERSAL_BASE(dev);
+
+ versal_realize_common(s);
versal_unimp(s);
+}
- /* Create the On Chip Memory (OCM). */
- memory_region_init_ram(&s->lpd.mr_ocm, OBJECT(s), "ocm",
- MM_OCM_SIZE, &error_fatal);
+static void versal2_realize(DeviceState *dev, Error **errp)
+{
+ Versal *s = XLNX_VERSAL_BASE(dev);
+
+ versal_realize_common(s);
+ versal2_unimp(s);
+}
+
+DeviceState *versal_get_boot_cpu(Versal *s)
+{
+ return DEVICE(versal_get_child_idx(s, "apu-cluster/apu", 0));
+}
+
+void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk)
+{
+ DeviceState *sdhci, *card;
+
+ sdhci = DEVICE(versal_get_child_idx(s, "sdhci", sd_idx));
+
+ if (sdhci == NULL) {
+ return;
+ }
+
+ card = qdev_new(TYPE_SD_CARD);
+ object_property_add_child(OBJECT(sdhci), "card[*]", OBJECT(card));
+ qdev_prop_set_drive_err(card, "drive", blk, &error_fatal);
+ qdev_realize_and_unref(card, qdev_get_child_bus(DEVICE(sdhci), "sd-bus"),
+ &error_fatal);
+}
+
+void versal_efuse_attach_drive(Versal *s, BlockBackend *blk)
+{
+ DeviceState *efuse;
+
+ efuse = DEVICE(versal_get_child(s, "efuse"));
+
+ if (efuse == NULL) {
+ return;
+ }
+
+ qdev_prop_set_drive(efuse, "drive", blk);
+}
+
+void versal_bbram_attach_drive(Versal *s, BlockBackend *blk)
+{
+ DeviceState *bbram;
+
+ bbram = DEVICE(versal_get_child(s, "bbram"));
+
+ if (bbram == NULL) {
+ return;
+ }
+
+ qdev_prop_set_drive(bbram, "drive", blk);
+}
+
+void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl,
+ BlockBackend *blk)
+{
+ BusState *spi_bus;
+ DeviceState *flash, *ospi;
+ qemu_irq cs_line;
+
+ ospi = DEVICE(versal_get_child(s, "ospi"));
+ spi_bus = qdev_get_child_bus(ospi, "spi0");
+
+ flash = qdev_new(flash_mdl);
- memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0);
- memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0);
- memory_region_add_subregion_overlap(&s->lpd.rpu.mr, 0,
- &s->lpd.rpu.mr_ps_alias, 0);
+ if (blk) {
+ qdev_prop_set_drive_err(flash, "drive", blk, &error_fatal);
+ }
+ qdev_prop_set_uint8(flash, "cs", flash_idx);
+ qdev_realize_and_unref(flash, spi_bus, &error_fatal);
+
+ cs_line = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(ospi),
+ flash_idx + 1, cs_line);
+}
+
+qemu_irq versal_get_reserved_irq(Versal *s, int idx, int *dtb_idx)
+{
+ const VersalMap *map = versal_get_map(s);
+
+ g_assert(idx < map->reserved.irq_num);
+
+ *dtb_idx = map->reserved.irq_start + idx;
+ return versal_get_irq(s, *dtb_idx);
}
-static void versal_init(Object *obj)
+hwaddr versal_get_reserved_mmio_addr(Versal *s)
{
- Versal *s = XLNX_VERSAL(obj);
+ const VersalMap *map = versal_get_map(s);
+
+ return map->reserved.mmio_start;
+}
+
+int versal_get_num_cpu(VersalVersion version)
+{
+ const VersalMap *map = VERSION_TO_MAP[version];
+
+ return map->apu.num_cluster * map->apu.num_core
+ + map->rpu.num_cluster * map->rpu.num_core;
+}
+
+int versal_get_num_can(VersalVersion version)
+{
+ const VersalMap *map = VERSION_TO_MAP[version];
+
+ return map->num_canfd;
+}
+
+int versal_get_num_sdhci(VersalVersion version)
+{
+ const VersalMap *map = VERSION_TO_MAP[version];
+
+ return map->num_sdhci;
+}
+
+static void versal_base_init(Object *obj)
+{
+ Versal *s = XLNX_VERSAL_BASE(obj);
+ size_t i, num_can;
- memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX);
- memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX);
memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX);
- memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s),
- "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX);
+ s->intc = g_array_new(false, false, sizeof(DeviceState *));
+
+ num_can = versal_get_map(s)->num_canfd;
+ s->cfg.canbus = g_new0(CanBusState *, num_can);
+
+ for (i = 0; i < num_can; i++) {
+ g_autofree char *prop_name = g_strdup_printf("canbus%zu", i);
+
+ object_property_add_link(obj, prop_name, TYPE_CAN_BUS,
+ (Object **) &s->cfg.canbus[i],
+ object_property_allow_set_link, 0);
+ }
+}
+
+static void versal_base_finalize(Object *obj)
+{
+ Versal *s = XLNX_VERSAL_BASE(obj);
+
+ g_array_free(s->intc, true);
+ g_free(s->cfg.canbus);
}
static const Property versal_properties[] = {
DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION,
MemoryRegion *),
- DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0],
- TYPE_CAN_BUS, CanBusState *),
- DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1],
- TYPE_CAN_BUS, CanBusState *),
};
-static void versal_class_init(ObjectClass *klass, const void *data)
+static void versal_base_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->realize = versal_realize;
device_class_set_props(dc, versal_properties);
/* No VMSD since we haven't got any top-level SoC state to save. */
}
-static const TypeInfo versal_info = {
- .name = TYPE_XLNX_VERSAL,
+static void versal_class_init(ObjectClass *klass, const void *data)
+{
+ VersalClass *vc = XLNX_VERSAL_BASE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ vc->version = VERSAL_VER_VERSAL;
+ dc->realize = versal_realize;
+}
+
+static void versal2_class_init(ObjectClass *klass, const void *data)
+{
+ VersalClass *vc = XLNX_VERSAL_BASE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ vc->version = VERSAL_VER_VERSAL2;
+ dc->realize = versal2_realize;
+}
+
+static const TypeInfo versal_base_info = {
+ .name = TYPE_XLNX_VERSAL_BASE,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Versal),
- .instance_init = versal_init,
+ .instance_init = versal_base_init,
+ .instance_finalize = versal_base_finalize,
+ .class_init = versal_base_class_init,
+ .class_size = sizeof(VersalClass),
+ .abstract = true,
+};
+
+static const TypeInfo versal_info = {
+ .name = TYPE_XLNX_VERSAL,
+ .parent = TYPE_XLNX_VERSAL_BASE,
.class_init = versal_class_init,
};
+static const TypeInfo versal2_info = {
+ .name = TYPE_XLNX_VERSAL2,
+ .parent = TYPE_XLNX_VERSAL_BASE,
+ .class_init = versal2_class_init,
+};
+
static void versal_register_types(void)
{
+ type_register_static(&versal_base_info);
type_register_static(&versal_info);
+ type_register_static(&versal2_info);
}
type_init(versal_register_types);