* [PATCH 1/4] Add MEN Chameleon Bus emulation
2023-03-29 8:45 [PATCH 0/4] Add emulation of MEN Chameleon Hardware Johannes Thumshirn
@ 2023-03-29 8:45 ` Johannes Thumshirn
2023-04-05 9:09 ` Alistair Francis
2023-03-29 8:45 ` [PATCH 2/4] Add MEN Chameleon Bus via PCI carrier Johannes Thumshirn
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Johannes Thumshirn @ 2023-03-29 8:45 UTC (permalink / raw)
To: qemu-devel
Cc: Alistair Francis, Javier Rodriguez, Dmitry Fomichev,
Johannes Thumshirn
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
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/4] Add MEN Chameleon Bus emulation
2023-03-29 8:45 ` [PATCH 1/4] Add MEN Chameleon Bus emulation Johannes Thumshirn
@ 2023-04-05 9:09 ` Alistair Francis
0 siblings, 0 replies; 8+ messages in thread
From: Alistair Francis @ 2023-04-05 9:09 UTC (permalink / raw)
To: Johannes Thumshirn
Cc: qemu-devel, Alistair Francis, Javier Rodriguez, Dmitry Fomichev
On Wed, Mar 29, 2023 at 11:08 PM Johannes Thumshirn <jth@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 <jth@kernel.org>
I don't know a lot about MEN Chameleon Bus, but this looks fine to me
Acked-by: Alistair Francis <alistair.francis@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 <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
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/4] Add MEN Chameleon Bus via PCI carrier
2023-03-29 8:45 [PATCH 0/4] Add emulation of MEN Chameleon Hardware Johannes Thumshirn
2023-03-29 8:45 ` [PATCH 1/4] Add MEN Chameleon Bus emulation Johannes Thumshirn
@ 2023-03-29 8:45 ` Johannes Thumshirn
2023-04-05 6:29 ` Alistair Francis
2023-03-29 8:45 ` [PATCH 3/4] serial-mcb: Add serial via MEN chameleon bus Johannes Thumshirn
2023-03-29 8:45 ` [PATCH 4/4] wdt_z069: Add support for MEN 16z069 Watchdog Johannes Thumshirn
3 siblings, 1 reply; 8+ messages in thread
From: Johannes Thumshirn @ 2023-03-29 8:45 UTC (permalink / raw)
To: qemu-devel
Cc: Alistair Francis, Javier Rodriguez, Dmitry Fomichev,
Johannes Thumshirn
Add PCI based MEN Chameleon Bus carrier emulation.
Signed-off-by: Johannes Thumshirn <jth@kernel.org>
---
hw/mcb/Kconfig | 6 +
hw/mcb/mcb-pci.c | 307 +++++++++++++++++++++++++++++++++++++++++++++
hw/mcb/meson.build | 1 +
3 files changed, 314 insertions(+)
create mode 100644 hw/mcb/mcb-pci.c
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..442e65e24c
--- /dev/null
+++ b/hw/mcb/mcb-pci.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "hw/mcb/mcb.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_device.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+
+/* #define DEBUG_MPCI 1 */
+
+#ifdef DEBUG_MPCI
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "mcb-pci: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+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;
+ uint32_t ret = 0;
+
+ DPRINTF("Read from address 0x%lx size %d\n", 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 */
+ DPRINTF("Last element: 0x%08x\n", ~0U);
+ return ~0U;
+ }
+ }
+ qdev = kid->child;
+ mdev = MCB_DEVICE(qdev);
+ offset -= slot * 16;
+
+ ret = read_gdd(mdev, offset / 4);
+ return le32_to_cpu(ret);
+ }
+
+ return ret;
+}
+
+static void mpci_chamtbl_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+
+ if (addr < CHAMELEON_TABLE_SIZE)
+ DPRINTF("Invalid write to 0x%x: 0x%x\n", (unsigned) addr,
+ (unsigned) 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'))
--
2.39.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/4] Add MEN Chameleon Bus via PCI carrier
2023-03-29 8:45 ` [PATCH 2/4] Add MEN Chameleon Bus via PCI carrier Johannes Thumshirn
@ 2023-04-05 6:29 ` Alistair Francis
0 siblings, 0 replies; 8+ messages in thread
From: Alistair Francis @ 2023-04-05 6:29 UTC (permalink / raw)
To: Johannes Thumshirn
Cc: qemu-devel, Alistair Francis, Javier Rodriguez, Dmitry Fomichev
On Wed, Mar 29, 2023 at 11:09 PM Johannes Thumshirn <jth@kernel.org> wrote:
>
> Add PCI based MEN Chameleon Bus carrier emulation.
>
> Signed-off-by: Johannes Thumshirn <jth@kernel.org>
> ---
> hw/mcb/Kconfig | 6 +
> hw/mcb/mcb-pci.c | 307 +++++++++++++++++++++++++++++++++++++++++++++
> hw/mcb/meson.build | 1 +
> 3 files changed, 314 insertions(+)
> create mode 100644 hw/mcb/mcb-pci.c
>
> 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..442e65e24c
> --- /dev/null
> +++ b/hw/mcb/mcb-pci.c
> @@ -0,0 +1,307 @@
> +/*
> + * 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 "hw/mcb/mcb.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_device.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +
> +/* #define DEBUG_MPCI 1 */
> +
> +#ifdef DEBUG_MPCI
> +#define DPRINTF(fmt, ...) \
> + do { fprintf(stderr, "mcb-pci: " fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) do { } while (0)
> +#endif
We don't use these #ifdefs for debug prints anymore in QEMU (at least
not in new code). It should be converted to the tracing architecture.
See https://qemu-project.gitlab.io/qemu/devel/tracing.html for user
documentation.
> +
> +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;
> + uint32_t ret = 0;
> +
> + DPRINTF("Read from address 0x%lx size %d\n", addr, size);
> +
> + if (addr < sizeof(ChameleonFPGAHeader)) {
> + return le32_to_cpu(read_header(s, addr));
I always thought you don't need the le32_to_cpu() as the
DEVICE_LITTLE_ENDIAN should handle this.
I do see other PCI devices with it though, so maybe it's a PCI thing
> + } 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 */
> + DPRINTF("Last element: 0x%08x\n", ~0U);
> + return ~0U;
> + }
> + }
> + qdev = kid->child;
> + mdev = MCB_DEVICE(qdev);
> + offset -= slot * 16;
> +
> + ret = read_gdd(mdev, offset / 4);
> + return le32_to_cpu(ret);
> + }
> +
> + return ret;
ret isn't used here
> +}
> +
> +static void mpci_chamtbl_write(void *opaque, hwaddr addr, uint64_t val,
> + unsigned size)
> +{
> +
> + if (addr < CHAMELEON_TABLE_SIZE)
> + DPRINTF("Invalid write to 0x%x: 0x%x\n", (unsigned) addr,
> + (unsigned) val);
This isn't Linux, brackets are required :)
You can run checkpatch to catch these types of style issues.
Alistair
> +
> + 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'))
> --
> 2.39.2
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/4] serial-mcb: Add serial via MEN chameleon bus
2023-03-29 8:45 [PATCH 0/4] Add emulation of MEN Chameleon Hardware Johannes Thumshirn
2023-03-29 8:45 ` [PATCH 1/4] Add MEN Chameleon Bus emulation Johannes Thumshirn
2023-03-29 8:45 ` [PATCH 2/4] Add MEN Chameleon Bus via PCI carrier Johannes Thumshirn
@ 2023-03-29 8:45 ` Johannes Thumshirn
2023-04-05 8:32 ` Alistair Francis
2023-03-29 8:45 ` [PATCH 4/4] wdt_z069: Add support for MEN 16z069 Watchdog Johannes Thumshirn
3 siblings, 1 reply; 8+ messages in thread
From: Johannes Thumshirn @ 2023-03-29 8:45 UTC (permalink / raw)
To: qemu-devel
Cc: Alistair Francis, Javier Rodriguez, Dmitry Fomichev,
Johannes Thumshirn
Add MEN z125 UART over MEN Chameleon Bus emulation.
Signed-off-by: Johannes Thumshirn <jth@kernel.org>
---
hw/char/Kconfig | 6 +++
hw/char/meson.build | 1 +
hw/char/serial-mcb.c | 115 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 122 insertions(+)
create mode 100644 hw/char/serial-mcb.c
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 6b6cf2fc1d..9e8ebf1d3d 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -71,3 +71,9 @@ config GOLDFISH_TTY
config SHAKTI_UART
bool
+
+config SERIAL_MCB
+ bool
+ default y if MCB
+ depends on MCB
+ select SERIAL
diff --git a/hw/char/meson.build b/hw/char/meson.build
index e02c60dd54..d5893a142d 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -20,6 +20,7 @@ softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c'))
softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c'))
softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c'))
+softmmu_ss.add(when: 'CONFIG_SERIAL_MCB', if_true: files('serial-mcb.c'))
softmmu_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
diff --git a/hw/char/serial-mcb.c b/hw/char/serial-mcb.c
new file mode 100644
index 0000000000..7ca8a248ac
--- /dev/null
+++ b/hw/char/serial-mcb.c
@@ -0,0 +1,115 @@
+/*
+ * QEMU MEN 16z125 UART over MCB 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/char/serial.h"
+#include "hw/mcb/mcb.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "migration/vmstate.h"
+
+struct MCBSerialState {
+ MCBDevice dev;
+ SerialState state;
+};
+
+#define TYPE_MCB_SERIAL "mcb-serial"
+OBJECT_DECLARE_SIMPLE_TYPE(MCBSerialState, MCB_SERIAL)
+
+static void serial_mcb_realize(DeviceState *dev, Error **errp)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+ MCBSerialState *mss = DO_UPCAST(MCBSerialState, dev, mdev);
+ MCBus *bus = MCB_BUS(qdev_get_parent_bus(DEVICE(dev)));
+ SerialState *s = &mss->state;
+
+ mdev->gdd = mcb_new_chameleon_descriptor(bus, 125, mdev->rev,
+ mdev->var, 0x10);
+ if (!mdev->gdd) {
+ return;
+ }
+
+ s->baudbase = 115200;
+ if (!qdev_realize(DEVICE(s), NULL, errp)) {
+ return;
+ }
+
+ s->irq = mcb_allocate_irq(&mss->dev);
+ memory_region_init_io(&s->io, OBJECT(mss), &serial_io_ops, s, "serial", 8);
+ memory_region_add_subregion(&bus->mmio_region, mdev->gdd->offset, &s->io);
+}
+
+static void serial_mcb_unrealize(DeviceState *dev)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+ MCBSerialState *mss = DO_UPCAST(MCBSerialState, dev, mdev);
+ SerialState *s = &mss->state;
+
+ qdev_unrealize(DEVICE(s));
+ qemu_free_irq(s->irq);
+ g_free(&mdev->gdd);
+}
+
+static const VMStateDescription vmstate_mcb_serial = {
+ .name = "mcb-serial",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MCB_DEVICE(dev, MCBSerialState),
+ VMSTATE_STRUCT(state, MCBSerialState, 0, vmstate_serial, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property serial_mcb_properties[] = {
+ DEFINE_PROP_UINT8("rev", MCBSerialState, dev.rev, 0),
+ DEFINE_PROP_UINT8("var", MCBSerialState, dev.var, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void serial_mcb_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ MCBDeviceClass *mc = MCB_DEVICE_CLASS(klass);
+
+ mc->realize = serial_mcb_realize;
+ mc->unrealize = serial_mcb_unrealize;
+
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ dc->desc = "MEN 16z125 UART over MCB";
+ dc->vmsd = &vmstate_mcb_serial;
+ device_class_set_props(dc, serial_mcb_properties);
+}
+
+static void serial_mcb_init(Object *o)
+{
+ MCBSerialState *mss = MCB_SERIAL(o);
+
+ object_initialize_child(o, "serial", &mss->state, TYPE_SERIAL);
+
+ qdev_alias_all_properties(DEVICE(&mss->state), o);
+}
+
+static const TypeInfo serial_mcb_info = {
+ .name = "mcb-serial",
+ .parent = TYPE_MCB_DEVICE,
+ .instance_size = sizeof(MCBSerialState),
+ .instance_init = serial_mcb_init,
+ .class_init = serial_mcb_class_initfn,
+};
+
+static void serial_mcb_register_types(void)
+{
+ type_register_static(&serial_mcb_info);
+}
+
+type_init(serial_mcb_register_types);
--
2.39.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 3/4] serial-mcb: Add serial via MEN chameleon bus
2023-03-29 8:45 ` [PATCH 3/4] serial-mcb: Add serial via MEN chameleon bus Johannes Thumshirn
@ 2023-04-05 8:32 ` Alistair Francis
0 siblings, 0 replies; 8+ messages in thread
From: Alistair Francis @ 2023-04-05 8:32 UTC (permalink / raw)
To: Johannes Thumshirn
Cc: qemu-devel, Alistair Francis, Javier Rodriguez, Dmitry Fomichev
On Wed, Mar 29, 2023 at 11:09 PM Johannes Thumshirn <jth@kernel.org> wrote:
>
> Add MEN z125 UART over MEN Chameleon Bus emulation.
>
> Signed-off-by: Johannes Thumshirn <jth@kernel.org>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> hw/char/Kconfig | 6 +++
> hw/char/meson.build | 1 +
> hw/char/serial-mcb.c | 115 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 122 insertions(+)
> create mode 100644 hw/char/serial-mcb.c
>
> diff --git a/hw/char/Kconfig b/hw/char/Kconfig
> index 6b6cf2fc1d..9e8ebf1d3d 100644
> --- a/hw/char/Kconfig
> +++ b/hw/char/Kconfig
> @@ -71,3 +71,9 @@ config GOLDFISH_TTY
>
> config SHAKTI_UART
> bool
> +
> +config SERIAL_MCB
> + bool
> + default y if MCB
> + depends on MCB
> + select SERIAL
> diff --git a/hw/char/meson.build b/hw/char/meson.build
> index e02c60dd54..d5893a142d 100644
> --- a/hw/char/meson.build
> +++ b/hw/char/meson.build
> @@ -20,6 +20,7 @@ softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c'))
> softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c'))
> softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c'))
> softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c'))
> +softmmu_ss.add(when: 'CONFIG_SERIAL_MCB', if_true: files('serial-mcb.c'))
>
> softmmu_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
> softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
> diff --git a/hw/char/serial-mcb.c b/hw/char/serial-mcb.c
> new file mode 100644
> index 0000000000..7ca8a248ac
> --- /dev/null
> +++ b/hw/char/serial-mcb.c
> @@ -0,0 +1,115 @@
> +/*
> + * QEMU MEN 16z125 UART over MCB 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/char/serial.h"
> +#include "hw/mcb/mcb.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "migration/vmstate.h"
> +
> +struct MCBSerialState {
> + MCBDevice dev;
> + SerialState state;
> +};
> +
> +#define TYPE_MCB_SERIAL "mcb-serial"
> +OBJECT_DECLARE_SIMPLE_TYPE(MCBSerialState, MCB_SERIAL)
> +
> +static void serial_mcb_realize(DeviceState *dev, Error **errp)
> +{
> + MCBDevice *mdev = MCB_DEVICE(dev);
> + MCBSerialState *mss = DO_UPCAST(MCBSerialState, dev, mdev);
> + MCBus *bus = MCB_BUS(qdev_get_parent_bus(DEVICE(dev)));
> + SerialState *s = &mss->state;
> +
> + mdev->gdd = mcb_new_chameleon_descriptor(bus, 125, mdev->rev,
> + mdev->var, 0x10);
> + if (!mdev->gdd) {
> + return;
> + }
> +
> + s->baudbase = 115200;
> + if (!qdev_realize(DEVICE(s), NULL, errp)) {
> + return;
> + }
> +
> + s->irq = mcb_allocate_irq(&mss->dev);
> + memory_region_init_io(&s->io, OBJECT(mss), &serial_io_ops, s, "serial", 8);
> + memory_region_add_subregion(&bus->mmio_region, mdev->gdd->offset, &s->io);
> +}
> +
> +static void serial_mcb_unrealize(DeviceState *dev)
> +{
> + MCBDevice *mdev = MCB_DEVICE(dev);
> + MCBSerialState *mss = DO_UPCAST(MCBSerialState, dev, mdev);
> + SerialState *s = &mss->state;
> +
> + qdev_unrealize(DEVICE(s));
> + qemu_free_irq(s->irq);
> + g_free(&mdev->gdd);
> +}
> +
> +static const VMStateDescription vmstate_mcb_serial = {
> + .name = "mcb-serial",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_MCB_DEVICE(dev, MCBSerialState),
> + VMSTATE_STRUCT(state, MCBSerialState, 0, vmstate_serial, SerialState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static Property serial_mcb_properties[] = {
> + DEFINE_PROP_UINT8("rev", MCBSerialState, dev.rev, 0),
> + DEFINE_PROP_UINT8("var", MCBSerialState, dev.var, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void serial_mcb_class_initfn(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + MCBDeviceClass *mc = MCB_DEVICE_CLASS(klass);
> +
> + mc->realize = serial_mcb_realize;
> + mc->unrealize = serial_mcb_unrealize;
> +
> + set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
> + dc->desc = "MEN 16z125 UART over MCB";
> + dc->vmsd = &vmstate_mcb_serial;
> + device_class_set_props(dc, serial_mcb_properties);
> +}
> +
> +static void serial_mcb_init(Object *o)
> +{
> + MCBSerialState *mss = MCB_SERIAL(o);
> +
> + object_initialize_child(o, "serial", &mss->state, TYPE_SERIAL);
> +
> + qdev_alias_all_properties(DEVICE(&mss->state), o);
> +}
> +
> +static const TypeInfo serial_mcb_info = {
> + .name = "mcb-serial",
> + .parent = TYPE_MCB_DEVICE,
> + .instance_size = sizeof(MCBSerialState),
> + .instance_init = serial_mcb_init,
> + .class_init = serial_mcb_class_initfn,
> +};
> +
> +static void serial_mcb_register_types(void)
> +{
> + type_register_static(&serial_mcb_info);
> +}
> +
> +type_init(serial_mcb_register_types);
> --
> 2.39.2
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/4] wdt_z069: Add support for MEN 16z069 Watchdog
2023-03-29 8:45 [PATCH 0/4] Add emulation of MEN Chameleon Hardware Johannes Thumshirn
` (2 preceding siblings ...)
2023-03-29 8:45 ` [PATCH 3/4] serial-mcb: Add serial via MEN chameleon bus Johannes Thumshirn
@ 2023-03-29 8:45 ` Johannes Thumshirn
3 siblings, 0 replies; 8+ messages in thread
From: Johannes Thumshirn @ 2023-03-29 8:45 UTC (permalink / raw)
To: qemu-devel
Cc: Alistair Francis, Javier Rodriguez, Dmitry Fomichev,
Johannes Thumshirn
Add 16z069 Watchdog over MEN Chameleon BUS emulation.
Signed-off-by: Johannes Thumshirn <jth@kernel.org>
---
hw/watchdog/Kconfig | 5 +
hw/watchdog/meson.build | 1 +
hw/watchdog/wdt_z069.c | 218 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 224 insertions(+)
create mode 100644 hw/watchdog/wdt_z069.c
diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig
index 66e1d029e3..a3f1196f66 100644
--- a/hw/watchdog/Kconfig
+++ b/hw/watchdog/Kconfig
@@ -20,3 +20,8 @@ config WDT_IMX2
config WDT_SBSA
bool
+
+config WDT_Z069
+ bool
+ default y if MCB
+ depends on MCB
diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build
index 8974b5cf4c..7bc353774e 100644
--- a/hw/watchdog/meson.build
+++ b/hw/watchdog/meson.build
@@ -6,4 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c'))
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c'))
softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c'))
softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c'))
+softmmu_ss.add(when: 'CONFIG_WDT_Z069', if_true: files('wdt_z069.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c'))
diff --git a/hw/watchdog/wdt_z069.c b/hw/watchdog/wdt_z069.c
new file mode 100644
index 0000000000..cb67519032
--- /dev/null
+++ b/hw/watchdog/wdt_z069.c
@@ -0,0 +1,218 @@
+/*
+ * QEMU MEN 16z069 Watchdog over MCB 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 "qemu/module.h"
+#include "qemu/timer.h"
+#include "sysemu/watchdog.h"
+#include "hw/mcb/mcb.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+
+/* #define Z069_DEBUG 1 */
+
+#ifdef Z069_DEBUG
+#define z069_debug(fmt, ...) \
+ fprintf(stderr, "wdt_z069: %s: "fmt, __func__, ##__VA_ARGS__)
+#else
+#define z069_debug(fmt, ...)
+#endif
+
+#define MEN_Z069_WTR 0x10
+#define MEN_Z069_WTR_WDEN BIT(15)
+#define MEN_Z069_WTR_WDET_MASK 0x7fff
+#define MEN_Z069_WVR 0x14
+
+#define CLK_500(x) ((x) * 2) /* 500Hz in ms */
+
+typedef struct {
+ /*< private >*/
+ MCBDevice dev;
+
+ /*< public >*/
+ QEMUTimer *timer;
+
+ bool enabled;
+ unsigned int timeout;
+
+ MemoryRegion mmio;
+
+ /* Registers */
+ uint16_t wtr;
+ uint16_t wvr;
+} MENZ069State;
+
+static void men_z069_wdt_enable(MENZ069State *s)
+{
+ z069_debug("next timeout will fire in +%dms\n", s->timeout);
+ timer_mod(s->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->timeout);
+}
+
+static void men_z069_wdt_disable(MENZ069State *s)
+{
+ timer_del(s->timer);
+}
+
+static uint64_t men_z069_wdt_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MENZ069State *s = opaque;
+ uint64_t ret;
+
+ switch (addr) {
+ case MEN_Z069_WTR:
+ ret = s->wtr;
+ break;
+ case MEN_Z069_WVR:
+ ret = s->wvr;
+ break;
+ default:
+ ret = 0UL;
+ break;
+ }
+
+ z069_debug("returning: 0x%"PRIx64" @ 0x%lx\n", ret, addr);
+ return ret;
+}
+
+static void men_z069_wdt_write(void *opaque, hwaddr addr, uint64_t v,
+ unsigned size)
+{
+ MENZ069State *s = opaque;
+ bool old_ena = s->enabled;
+ uint16_t val = v & 0xffff;
+ uint16_t tout;
+
+ z069_debug("got: 0x%"PRIx64" @ 0x%lx\n", v, addr);
+
+ switch (addr) {
+ case MEN_Z069_WTR:
+ s->wtr = val;
+ tout = val & MEN_Z069_WTR_WDET_MASK;
+ s->timeout = CLK_500(tout);
+ s->enabled = val & MEN_Z069_WTR_WDEN;
+ z069_debug("new timeout: %u (0x%x) %u\n", tout, tout, s->timeout);
+
+ if (old_ena && !s->enabled) {
+ men_z069_wdt_disable(s);
+ } else if (!old_ena && s->enabled) {
+ men_z069_wdt_enable(s);
+ }
+
+ break;
+ case MEN_Z069_WVR:
+ /* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
+ if (val == (s->wvr ^ 0xffff)) {
+ s->wvr = val;
+ z069_debug("watchdog triggered, next timeout will fire in +%dms\n",
+ s->timeout);
+ timer_mod(s->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->timeout);
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static const MemoryRegionOps men_z069_io_ops = {
+ .read = men_z069_wdt_read,
+ .write = men_z069_wdt_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 men_z069_timer_expired(void *opaque)
+{
+ MENZ069State *s = opaque;
+
+ watchdog_perform_action();
+ timer_del(s->timer);
+}
+
+static void men_z069_wdt_realize(DeviceState *dev, Error **errp)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+ MENZ069State *s = DO_UPCAST(MENZ069State, dev, mdev);
+ MCBus *bus = MCB_BUS(qdev_get_parent_bus(DEVICE(dev)));
+
+ mdev->gdd = mcb_new_chameleon_descriptor(bus, 69, mdev->rev,
+ mdev->var, 0x18);
+ if (!mdev->gdd) {
+ return;
+ }
+
+ s->wvr = 0x5555;
+ s->wtr = 0x7fff;
+ s->timeout = CLK_500(s->wtr & MEN_Z069_WTR_WDET_MASK);
+ s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ men_z069_timer_expired, s);
+
+ memory_region_init_io(&s->mmio, OBJECT(s),
+ &men_z069_io_ops, s, "z069.wdt", 0x16);
+ memory_region_add_subregion(&bus->mmio_region, mdev->gdd->offset,
+ &s->mmio);
+}
+
+static void men_z069_wdt_unrealize(DeviceState *dev)
+{
+ MCBDevice *mdev = MCB_DEVICE(dev);
+
+ g_free(&mdev->gdd);
+}
+
+static const VMStateDescription vmstate_z069_wdt = {
+ .name = "z069-wdt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MCB_DEVICE(dev, MENZ069State),
+ VMSTATE_TIMER_PTR(timer, MENZ069State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property men_z069_wdt_properties[] = {
+ DEFINE_PROP_UINT8("rev", MENZ069State, dev.rev, 0),
+ DEFINE_PROP_UINT8("var", MENZ069State, dev.var, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void men_z069_wdt_class_intifn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ MCBDeviceClass *mc = MCB_DEVICE_CLASS(klass);
+
+ mc->realize = men_z069_wdt_realize;
+ mc->unrealize = men_z069_wdt_unrealize;
+
+ dc->desc = "MEN 16z069 Watchdog Timer";
+ dc->vmsd = &vmstate_z069_wdt;
+ device_class_set_props(dc, men_z069_wdt_properties);
+}
+
+static const TypeInfo men_z069_wdt_info = {
+ .name = "z069-wdt",
+ .parent = TYPE_MCB_DEVICE,
+ .instance_size = sizeof(MENZ069State),
+ .class_init = men_z069_wdt_class_intifn,
+};
+
+static void men_z069_wdt_register_types(void)
+{
+ type_register_static(&men_z069_wdt_info);
+}
+
+type_init(men_z069_wdt_register_types);
--
2.39.2
^ permalink raw reply related [flat|nested] 8+ messages in thread