[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 1/4] Add MEN Chameleon Bus emulation
From: |
Johannes Thumshirn |
Subject: |
[PATCH 1/4] Add MEN Chameleon Bus emulation |
Date: |
Wed, 29 Mar 2023 10:45:10 +0200 |
The MEN Chameleon Bus (MCB) is an on-chip bus system exposing IP Cores of an
FPGA to a outside bus system like PCIe.
Signed-off-by: Johannes Thumshirn <jth@kernel.org>
---
MAINTAINERS | 6 ++
hw/Kconfig | 1 +
hw/mcb/Kconfig | 2 +
hw/mcb/mcb.c | 182 +++++++++++++++++++++++++++++++++++++++++++
hw/mcb/meson.build | 1 +
hw/meson.build | 1 +
include/hw/mcb/mcb.h | 106 +++++++++++++++++++++++++
7 files changed, 299 insertions(+)
create mode 100644 hw/mcb/Kconfig
create mode 100644 hw/mcb/mcb.c
create mode 100644 hw/mcb/meson.build
create mode 100644 include/hw/mcb/mcb.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 98cb2d64cf..badec8abdd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1947,6 +1947,12 @@ R: Paolo Bonzini <pbonzini@redhat.com>
S: Odd Fixes
F: hw/char/
+MEN Chameleon Bus
+M: Johannes Thumshirn <jth@kernel.org>
+S: Maintained
+F: hw/mcb/
+F: include/hw/mcb/
+
Network devices
M: Jason Wang <jasowang@redhat.com>
S: Odd Fixes
diff --git a/hw/Kconfig b/hw/Kconfig
index ba62ff6417..f5ef84b10b 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -18,6 +18,7 @@ source intc/Kconfig
source ipack/Kconfig
source ipmi/Kconfig
source isa/Kconfig
+source mcb/Kconfig
source mem/Kconfig
source misc/Kconfig
source net/Kconfig
diff --git a/hw/mcb/Kconfig b/hw/mcb/Kconfig
new file mode 100644
index 0000000000..36a7a583a8
--- /dev/null
+++ b/hw/mcb/Kconfig
@@ -0,0 +1,2 @@
+config MCB
+ bool
diff --git a/hw/mcb/mcb.c b/hw/mcb/mcb.c
new file mode 100644
index 0000000000..f2bf722de5
--- /dev/null
+++ b/hw/mcb/mcb.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU MEN Chameleon Bus emulation
+ *
+ * Copyright (C) 2023 Johannes Thumshirn <jth@kernel.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/mcb/mcb.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+
+ChameleonDeviceDescriptor *mcb_new_chameleon_descriptor(MCBus *bus, uint8_t id,
+ uint8_t rev,
+ uint8_t var,
+ uint32_t size)
+{
+ BusChild *kid;
+ ChameleonDeviceDescriptor *gdd;
+ uint32_t reg1 = 0;
+ uint32_t offset = 0x200;
+ uint32_t end = 0;
+
+ gdd = g_new0(ChameleonDeviceDescriptor, 1);
+ if (!gdd) {
+ return NULL;
+ }
+
+ reg1 |= GDD_DEV(id);
+ reg1 |= GDD_DTY(CHAMELEON_DTYPE_GENERAL);
+ reg1 |= GDD_REV(rev);
+ reg1 |= GDD_VAR(var);
+ gdd->reg1 = cpu_to_le32(reg1);
+
+ QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) {
+ DeviceState *qdev = kid->child;
+ MCBDevice *mdev = MCB_DEVICE(qdev);
+
+ if (mdev->gdd) {
+ offset = mdev->gdd->offset;
+ end = offset + mdev->gdd->size;
+ }
+ }
+
+ gdd->offset = offset + end;
+ gdd->size = size;
+
+ return gdd;
+}
+
+static void mcb_irq_handler(void *opaque, int irq_num, int level)
+{
+ MCBDevice *dev = opaque;
+ MCBus *bus = MCB_BUS(qdev_get_parent_bus(DEVICE(dev)));
+
+ if (bus->set_irq) {
+ bus->set_irq(dev, irq_num, level);
+ }
+}
+
+qemu_irq mcb_allocate_irq(MCBDevice *dev)
+{
+ int irq = 0;
+ return qemu_allocate_irq(mcb_irq_handler, dev, irq);
+}
+
+MCBDevice *mcb_device_find(MCBus *bus, hwaddr addr)
+{
+ BusChild *kid;
+ uint32_t start;
+ uint32_t end;
+
+ QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) {
+ DeviceState *qdev = kid->child;
+ MCBDevice *mdev = MCB_DEVICE(qdev);
+
+ start = mdev->gdd->offset;
+ end = start + mdev->gdd->size;
+
+ if (addr >= start && addr <= end) {
+ return mdev;
+ }
+ }
+ return NULL;
+}
+
+void mcb_bus_init(MCBus *bus, size_t bus_size,
+ DeviceState *parent,
+ uint8_t n_slots,
+ qemu_irq_handler handler)
+{
+ qbus_init(bus, bus_size, TYPE_MCB_BUS, parent, NULL);
+ bus->n_slots = n_slots;
+ bus->set_irq = handler;
+}
+
+static void mcb_device_realize(DeviceState *dev, Error **errp)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+ MCBus *bus = MCB_BUS(qdev_get_parent_bus(dev));
+ MCBDeviceClass *k = MCB_DEVICE_GET_CLASS(dev);
+
+ if (mdev->slot < 0) {
+ mdev->slot = bus->free_slot;
+ }
+
+ if (mdev->slot >= bus->n_slots) {
+ error_setg(errp, "Only %" PRIu8 " slots available.", bus->n_slots);
+ return;
+ }
+ bus->free_slot = mdev->slot + 1;
+
+ mdev->irq = qemu_allocate_irqs(bus->set_irq, mdev, 1);
+
+ k->realize(dev, errp);
+}
+
+static void mcb_device_unrealize(DeviceState *dev)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+ MCBDeviceClass *k = MCB_DEVICE_GET_CLASS(dev);
+
+ if (k->unrealize) {
+ k->unrealize(dev);
+ return;
+ }
+
+ qemu_free_irqs(mdev->irq, 1);
+}
+
+static Property mcb_device_props[] = {
+ DEFINE_PROP_INT32("slot", MCBDevice, slot, -1),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void mcb_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_INPUT, k->categories);
+ k->bus_type = TYPE_MCB_BUS;
+ k->realize = mcb_device_realize;
+ k->unrealize = mcb_device_unrealize;
+ device_class_set_props(k, mcb_device_props);
+}
+
+const VMStateDescription vmstate_mcb_device = {
+ .name = "mcb_device",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(slot, MCBDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const TypeInfo mcb_device_info = {
+ .name = TYPE_MCB_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(MCBDevice),
+ .class_size = sizeof(MCBDeviceClass),
+ .class_init = mcb_device_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo mcb_bus_info = {
+ .name = TYPE_MCB_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(MCBus),
+};
+
+static void mcb_register_types(void)
+{
+ type_register_static(&mcb_device_info);
+ type_register_static(&mcb_bus_info);
+}
+
+type_init(mcb_register_types);
diff --git a/hw/mcb/meson.build b/hw/mcb/meson.build
new file mode 100644
index 0000000000..a385edc07c
--- /dev/null
+++ b/hw/mcb/meson.build
@@ -0,0 +1 @@
+softmmu_ss.add(when: 'CONFIG_MCB', if_true: files('mcb.c'))
diff --git a/hw/meson.build b/hw/meson.build
index c7ac7d3d75..3d1462ad8b 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -18,6 +18,7 @@ subdir('intc')
subdir('ipack')
subdir('ipmi')
subdir('isa')
+subdir('mcb')
subdir('mem')
subdir('misc')
subdir('net')
diff --git a/include/hw/mcb/mcb.h b/include/hw/mcb/mcb.h
new file mode 100644
index 0000000000..ff120073e1
--- /dev/null
+++ b/include/hw/mcb/mcb.h
@@ -0,0 +1,106 @@
+/*
+ * QEMU MEN Chameleon Bus emulation
+ *
+ * Copyright (C) 2023 Johannes Thumshirn <jth@kernel.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_MCB_H
+#define QEMU_MCB_H
+
+#include "hw/qdev-core.h"
+#include "qom/object.h"
+#include "exec/memory.h"
+
+#define CHAMELEON_DTYPE_GENERAL 0x0
+#define CHAMELEON_DTYPE_END 0xf
+
+typedef struct {
+ uint32_t reg1;
+ uint32_t reg2;
+ uint32_t offset;
+ uint32_t size;
+} ChameleonDeviceDescriptor;
+
+#define GDD_DEV(x) (((x) & 0x3ff) << 18)
+#define GDD_DTY(x) (((x) & 0xf) << 28)
+#define GDD_REV(x) (((x) & 0x3f) << 5)
+#define GDD_VAR(x) (((x) & 0x3f) << 11)
+
+/* GDD Register 1 fields */
+#define GDD_IRQ(x) ((x) & 0x1f)
+
+/* GDD Register 2 fields */
+#define GDD_BAR(x) ((x) & 0x7)
+#define GDD_INS(x) (((x) >> 3) & 0x3f)
+#define GDD_GRP(x) (((x) >> 9) & 0x3f)
+
+typedef struct MCBus MCBus;
+
+#define TYPE_MCB_BUS "MEN Chameleon Bus"
+OBJECT_DECLARE_SIMPLE_TYPE(MCBus, MCB_BUS)
+
+struct MCBus {
+ /*< private >*/
+ BusState parent_obj;
+
+ uint8_t n_slots;
+ uint8_t free_slot;
+ qemu_irq_handler set_irq;
+ MemoryRegion mmio_region;
+};
+
+typedef struct MCBDevice MCBDevice;
+typedef struct MCBDeviceClass MCBDeviceClass;
+
+#define TYPE_MCB_DEVICE "mcb-device"
+#define MCB_DEVICE(obj) \
+ OBJECT_CHECK(MCBDevice, (obj), TYPE_MCB_DEVICE)
+#define MCB_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(MCBDeviceClass, (klass), TYPE_MCB_DEVICE)
+#define MCB_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(MCBDeviceClass, (obj), TYPE_MCB_DEVICE)
+
+struct MCBDeviceClass {
+ /*< private >*/
+ DeviceClass parent_class;
+ /*< public >*/
+
+
+ DeviceRealize realize;
+ DeviceUnrealize unrealize;
+};
+
+struct MCBDevice {
+ /*< private >*/
+ DeviceState parent_obj;
+ /*< public >*/
+
+ qemu_irq *irq;
+ ChameleonDeviceDescriptor *gdd;
+ int slot;
+
+ uint8_t rev;
+ uint8_t var;
+};
+
+extern const VMStateDescription vmstate_mcb_device;
+
+ChameleonDeviceDescriptor *mcb_new_chameleon_descriptor(MCBus *bus, uint8_t id,
+ uint8_t rev,
+ uint8_t var,
+ uint32_t size);
+
+#define VMSTATE_MCB_DEVICE(_field, _state) \
+ VMSTATE_STRUCT(_field, _state, 1, vmstate_mcb_device, MCBDevice)
+
+MCBDevice *mcb_device_find(MCBus *bus, hwaddr addr);
+void mcb_bus_init(MCBus *bus, size_t bus_size,
+ DeviceState *parent,
+ uint8_t n_slots,
+ qemu_irq_handler handler);
+
+qemu_irq mcb_allocate_irq(MCBDevice *dev);
+#endif
--
2.39.2