On Wed, Mar 29, 2023 at 11:08 PM Johannes Thumshirn <j...@kernel.org> wrote: > > 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 <j...@kernel.org>
I don't know a lot about MEN Chameleon Bus, but this looks fine to me Acked-by: Alistair Francis <alistair.fran...@wdc.com> Alistair > --- > 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 <pbonz...@redhat.com> > S: Odd Fixes > F: hw/char/ > > +MEN Chameleon Bus > +M: Johannes Thumshirn <j...@kernel.org> > +S: Maintained > +F: hw/mcb/ > +F: include/hw/mcb/ > + > Network devices > M: Jason Wang <jasow...@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 <j...@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 <j...@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 > >