[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v3 2/4] Add MEN Chameleon Bus via PCI carrier
From: |
Alistair Francis |
Subject: |
Re: [PATCH v3 2/4] Add MEN Chameleon Bus via PCI carrier |
Date: |
Mon, 17 Apr 2023 13:14:10 +1000 |
On Tue, Apr 11, 2023 at 3:51 AM Johannes Thumshirn <jth@kernel.org> wrote:
>
> Add PCI based MEN Chameleon Bus carrier emulation.
>
> Signed-off-by: Johannes Thumshirn <jth@kernel.org>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> hw/mcb/Kconfig | 6 +
> hw/mcb/mcb-pci.c | 297 ++++++++++++++++++++++++++++++++++++++++++++
> hw/mcb/meson.build | 1 +
> hw/mcb/trace-events | 3 +
> hw/mcb/trace.h | 1 +
> meson.build | 1 +
> 6 files changed, 309 insertions(+)
> create mode 100644 hw/mcb/mcb-pci.c
> create mode 100644 hw/mcb/trace-events
> create mode 100644 hw/mcb/trace.h
>
> diff --git a/hw/mcb/Kconfig b/hw/mcb/Kconfig
> index 36a7a583a8..7deb96c2fe 100644
> --- a/hw/mcb/Kconfig
> +++ b/hw/mcb/Kconfig
> @@ -1,2 +1,8 @@
> config MCB
> bool
> +
> +config MCB_PCI
> + bool
> + default y if PCI_DEVICES
> + depends on PCI
> + select MCB
> diff --git a/hw/mcb/mcb-pci.c b/hw/mcb/mcb-pci.c
> new file mode 100644
> index 0000000000..516f133c2e
> --- /dev/null
> +++ b/hw/mcb/mcb-pci.c
> @@ -0,0 +1,297 @@
> +/*
> + * QEMU MEN Chameleon Bus emulation
> + *
> + * Copyright (C) 2023 Johannes Thumshirn <jth@kernel.org>
> + *
> + * This code is licensed under the GPL version 2 or later. See the
> + * COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/mcb/mcb.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_device.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +#include "trace.h"
> +
> +typedef struct {
> + uint8_t revision;
> + char model;
> + uint8_t minor;
> + uint8_t bus_type;
> + uint16_t magic;
> + uint16_t reserved;
> + /* This one has no '\0' at the end!!! */
> + char filename[12];
> +} ChameleonFPGAHeader;
> +#define CHAMELEON_BUS_TYPE_WISHBONE 0
> +#define CHAMELEONV2_MAGIC 0xabce
> +
> +typedef struct {
> + PCIDevice dev;
> + MCBus bus;
> + MemoryRegion ctbl;
> + uint16_t status;
> + uint8_t int_set;
> + ChameleonFPGAHeader *header;
> +
> + uint8_t minor;
> + uint8_t rev;
> + uint8_t model;
> +} MPCIState;
> +
> +#define TYPE_MCB_PCI "mcb-pci"
> +
> +#define MPCI(obj) \
> + OBJECT_CHECK(MPCIState, (obj), TYPE_MCB_PCI)
> +
> +#define CHAMELEON_TABLE_SIZE 0x200
> +#define N_MODULES 32
> +
> +#define PCI_VENDOR_ID_MEN 0x1a88
> +#define PCI_DEVICE_ID_MEN_MCBPCI 0x4d45
> +
> +static uint32_t read_header(MPCIState *s, hwaddr addr)
> +{
> + uint32_t ret = 0;
> + ChameleonFPGAHeader *header = s->header;
> +
> + switch (addr >> 2) {
> + case 0:
> + ret |= header->revision;
> + ret |= header->model << 8;
> + ret |= header->minor << 16;
> + ret |= header->bus_type << 24;
> + break;
> + case 1:
> + ret |= header->magic;
> + ret |= header->reserved << 16;
> + break;
> + case 2:
> + memcpy(&ret, header->filename, sizeof(uint32_t));
> + break;
> + case 3:
> + memcpy(&ret, header->filename + sizeof(uint32_t),
> + sizeof(uint32_t));
> + break;
> + case 4:
> + memcpy(&ret, header->filename + 2 * sizeof(uint32_t),
> + sizeof(uint32_t));
> + }
> +
> + return ret;
> +}
> +
> +static uint32_t read_gdd(MCBDevice *mdev, int reg)
> +{
> + ChameleonDeviceDescriptor *gdd;
> + uint32_t ret = 0;
> +
> + gdd = mdev->gdd;
> +
> + switch (reg) {
> + case 0:
> + ret = gdd->reg1;
> + break;
> + case 1:
> + ret = gdd->reg2;
> + break;
> + case 2:
> + ret = gdd->offset;
> + break;
> + case 3:
> + ret = gdd->size;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static uint64_t mpci_chamtbl_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + MPCIState *s = opaque;
> + MCBus *bus = &s->bus;
> + MCBDevice *mdev;
> +
> + trace_mpci_chamtbl_read(addr, size);
> +
> + if (addr < sizeof(ChameleonFPGAHeader)) {
> + return le32_to_cpu(read_header(s, addr));
> + } else if (addr >= sizeof(ChameleonFPGAHeader) &&
> + addr < CHAMELEON_TABLE_SIZE) {
> + /* Handle read on chameleon table */
> + BusChild *kid;
> + DeviceState *qdev;
> + int slot;
> + int offset;
> + int i;
> +
> + offset = addr - sizeof(ChameleonFPGAHeader);
> + slot = offset / sizeof(ChameleonDeviceDescriptor);
> +
> + kid = QTAILQ_FIRST(&BUS(bus)->children);
> + for (i = 0; i < slot; i++) {
> + kid = QTAILQ_NEXT(kid, sibling);
> + if (!kid) { /* Last element */
> + return ~0U;
> + }
> + }
> + qdev = kid->child;
> + mdev = MCB_DEVICE(qdev);
> + offset -= slot * 16;
> +
> + return le32_to_cpu(read_gdd(mdev, offset / 4));
> + }
> +
> + return 0;
> +}
> +
> +static void mpci_chamtbl_write(void *opaque, hwaddr addr, uint64_t val,
> + unsigned size)
> +{
> +
> + if (addr < CHAMELEON_TABLE_SIZE) {
> + trace_mpci_chamtbl_write(addr, val);
> + }
> +
> + return;
> +}
> +
> +static const MemoryRegionOps mpci_chamtbl_ops = {
> + .read = mpci_chamtbl_read,
> + .write = mpci_chamtbl_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + },
> + .impl = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + },
> +};
> +
> +static void mcb_pci_set_irq(void *opaque, int intno, int level)
> +{
> + MCBDevice *mdev = opaque;
> + MCBus *bus = MCB_BUS(qdev_get_parent_bus(DEVICE(mdev)));
> + PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent);
> + MPCIState *dev = MPCI(pcidev);
> +
> + if (level) {
> + pci_set_irq(&dev->dev, !dev->int_set);
> + pci_set_irq(&dev->dev, dev->int_set);
> + } else {
> + uint16_t level_status = dev->status;
> +
> + if (level_status && !dev->int_set) {
> + pci_irq_assert(&dev->dev);
> + dev->int_set = 1;
> + } else if (!level_status && dev->int_set) {
> + pci_irq_deassert(&dev->dev);
> + dev->int_set = 0;
> + }
> + }
> +}
> +
> +static void mcb_pci_write_config(PCIDevice *pci_dev, uint32_t address,
> + uint32_t val, int len)
> +{
> + pci_default_write_config(pci_dev, address, val, len);
> +}
> +
> +static void mcb_pci_realize(PCIDevice *pci_dev, Error **errp)
> +{
> + MPCIState *s = MPCI(pci_dev);
> + uint8_t *pci_conf = s->dev.config;
> + ChameleonFPGAHeader *header;
> + MCBus *bus = &s->bus;
> +
> + header = g_new0(ChameleonFPGAHeader, 1);
> +
> + s->header = header;
> +
> + header->revision = s->rev;
> + header->model = (char) s->model;
> + header->minor = s->minor;
> + header->bus_type = CHAMELEON_BUS_TYPE_WISHBONE;
> + header->magic = CHAMELEONV2_MAGIC;
> + memcpy(&header->filename, "QEMU MCB PCI", 12);
> +
> + pci_dev->config_write = mcb_pci_write_config;
> + pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
> + pci_conf[PCI_COMMAND] = PCI_COMMAND_MEMORY;
> +
> + mcb_bus_init(bus, sizeof(MCBus), DEVICE(pci_dev), N_MODULES,
> + mcb_pci_set_irq);
> +
> + memory_region_init(&bus->mmio_region, OBJECT(s), "mcb-pci.mmio",
> + 2048 * 1024);
> + memory_region_init_io(&s->ctbl, OBJECT(s), &mpci_chamtbl_ops,
> + s, "mpci_chamtbl_ops", CHAMELEON_TABLE_SIZE);
> + memory_region_add_subregion(&bus->mmio_region, 0, &s->ctbl);
> + pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
> + &bus->mmio_region);
> +
> +}
> +
> +static void mcb_pci_unrealize(PCIDevice *pci_dev)
> +{
> + MPCIState *s = MPCI(pci_dev);
> +
> + g_free(s->header);
> + s->header = NULL;
> +}
> +
> +static const VMStateDescription vmstate_mcb_pci = {
> + .name = "mcb-pci",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_PCI_DEVICE(dev, MPCIState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static Property mcb_pci_props[] = {
> + DEFINE_PROP_UINT8("revision", MPCIState, rev, 1),
> + DEFINE_PROP_UINT8("minor", MPCIState, minor, 0),
> + DEFINE_PROP_UINT8("model", MPCIState, model, 0x41),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void mcb_pci_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> + k->realize = mcb_pci_realize;
> + k->exit = mcb_pci_unrealize;
> + k->vendor_id = PCI_VENDOR_ID_MEN;
> + k->device_id = PCI_DEVICE_ID_MEN_MCBPCI;
> + k->class_id = PCI_CLASS_BRIDGE_OTHER;
> +
> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> + dc->desc = "MEN Chameleon Bus over PCI";
> + dc->vmsd = &vmstate_mcb_pci;
> + device_class_set_props(dc, mcb_pci_props);
> +}
> +
> +static const TypeInfo mcb_pci_info = {
> + .name = TYPE_MCB_PCI,
> + .parent = TYPE_PCI_DEVICE,
> + .instance_size = sizeof(MPCIState),
> + .class_init = mcb_pci_class_init,
> + .interfaces = (InterfaceInfo[]) {
> + { INTERFACE_PCIE_DEVICE },
> + { }
> + },
> +};
> +
> +static void mcb_pci_register_types(void)
> +{
> + type_register(&mcb_pci_info);
> +}
> +type_init(mcb_pci_register_types);
> diff --git a/hw/mcb/meson.build b/hw/mcb/meson.build
> index a385edc07c..4e1a0f0cdb 100644
> --- a/hw/mcb/meson.build
> +++ b/hw/mcb/meson.build
> @@ -1 +1,2 @@
> softmmu_ss.add(when: 'CONFIG_MCB', if_true: files('mcb.c'))
> +softmmu_ss.add(when: 'CONFIG_MCB_PCI', if_true: files('mcb-pci.c'))
> diff --git a/hw/mcb/trace-events b/hw/mcb/trace-events
> new file mode 100644
> index 0000000000..e1adf9c8e3
> --- /dev/null
> +++ b/hw/mcb/trace-events
> @@ -0,0 +1,3 @@
> +# mcb-pci.c
> +mpci_chamtbl_read(unsigned long addr, unsigned int size) "read from address
> 0x%lx size %d"
> +mpci_chamtbl_write(unsigned long addr, uint64_t val) "invalid write to
> 0x%lx: 0x%" PRIx64
> diff --git a/hw/mcb/trace.h b/hw/mcb/trace.h
> new file mode 100644
> index 0000000000..35653b3381
> --- /dev/null
> +++ b/hw/mcb/trace.h
> @@ -0,0 +1 @@
> +#include "trace/trace-hw_mcb.h"
> diff --git a/meson.build b/meson.build
> index 29f8644d6d..ff8305440b 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2986,6 +2986,7 @@ if have_system
> 'hw/input',
> 'hw/intc',
> 'hw/isa',
> + 'hw/mcb',
> 'hw/mem',
> 'hw/mips',
> 'hw/misc',
> --
> 2.39.2
>
>