This patch adds an abstract platform device class. Real platform devices should use this as their parent class.
It manages IO memory regions and IRQ ranges for the actual device. Signed-off-by: Alexander Graf <ag...@suse.de> --- hw/platbus/device.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ include/hw/platbus/device.h | 102 ++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 hw/platbus/device.c create mode 100644 include/hw/platbus/device.h diff --git a/hw/platbus/device.c b/hw/platbus/device.c new file mode 100644 index 0000000..74ba11b --- /dev/null +++ b/hw/platbus/device.c @@ -0,0 +1,162 @@ +/* + * Platform Device that can connect to a Platform Bus + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Authors: Alexander Graf, <ag...@suse.de> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * + * This is an abstract platform device, so you really only want to use it + * as parent class for platform devices. + * + * It ensures that all boilerplate is properly abstracted away from children + * and consistent across devices. + * + * When instantiating a platform device you can optionally always specify 2 qdev + * properties which otherwise get populated automatically: + * + * regions: Offsets in the platbus hole the device's memory regions get mapped + * to. + * irqs: IRQ pins in the linear platbus IRQ range the device's IRQs get mapped + * to. + */ + +#include "qemu-common.h" +#include "hw/platbus/platbus.h" +#include "hw/platbus/device.h" + +static int realize_regions(PlatBusDeviceState *s, PlatBusState *bus, + Error **errp) +{ + uint64_t *addrs = g_new(uint64_t, s->num_regions); + int r, i; + + /* get our memory regions */ + for (i = 0; i < s->num_regions; i++) { + if (s->num_plat_region_addrs > i) { + /* user defined region */ + addrs[i] = s->plat_region_addrs[i]; + } else { + /* dynamically allocated region */ + addrs[i] = PLATBUS_DYNAMIC; + } + + r = platbus_map_region(bus, &addrs[i], s->regions[i]); + if (r) { + error_setg(errp, "PlatBus Device: Could not map region at %" PRIx64, + addrs[i]); + return -1; + } + } + + /* update our region list, so that we can see it with info qtree */ + s->plat_region_addrs = addrs; + s->num_plat_region_addrs = s->num_regions; + + return 0; +} + +static int realize_irqs(PlatBusDeviceState *s, PlatBusState *bus, Error **errp) +{ + uint32_t *irqs = g_new(uint32_t, s->num_irqs); + int r, i; + + /* get our IRQs */ + for (i = 0; i < s->num_irqs; i++) { + if (s->num_plat_irqs > i) { + /* user defined IRQ */ + irqs[i] = s->plat_irqs[i]; + } else { + /* dynamically allocated IRQ */ + irqs[i] = PLATBUS_DYNAMIC; + } + + r = platbus_map_irq(bus, &irqs[i], s->irqs[i]); + if (r) { + error_setg(errp, "PlatBus Device: Could not map IRQ %d", irqs[i]); + return -1; + } + } + + /* update our IRQ list, so that we can see it with info qtree */ + s->plat_irqs = irqs; + s->num_plat_irqs = s->num_irqs; + + return 0; +} + +static void platbus_device_realize(DeviceState *dev, Error **errp) +{ + PlatBusDeviceState *s = PLATBUS_DEVICE(dev); + PlatBusState *bus = PLAT_BUS(qdev_get_parent_bus(dev)); + + /* Register memory regions and IRQs */ + if (realize_regions(s, bus, errp) || realize_irqs(s, bus, errp)) { + return; + } + + /* Tell the bus that we're here */ + QTAILQ_INSERT_TAIL(&bus->devices, s, next); +} + +static Property platbus_device_properties[] = { + /* memory regions for a device */ + DEFINE_PROP_ARRAY("regions", PlatBusDeviceState, num_plat_region_addrs, + plat_region_addrs, qdev_prop_uint64, uint64_t), + /* interrupts for a device */ + DEFINE_PROP_ARRAY("irqs", PlatBusDeviceState, num_plat_irqs, + plat_irqs, qdev_prop_uint32, uint32_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void platbus_device_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = platbus_device_realize; + dc->bus_type = TYPE_PLAT_BUS; + dc->props = platbus_device_properties; +} + +static const TypeInfo platbus_device_type_info = { + .name = TYPE_PLATBUS_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PlatBusDeviceState), + .abstract = true, + .class_init = platbus_device_class_init, +}; + + +static void device_register_types(void) +{ + type_register_static(&platbus_device_type_info); +} + +type_init(device_register_types) + +/******** public methods *********/ + +void platbus_device_set_irqs_array(PlatBusDeviceState *dev, int n, + qemu_irq **irqs) +{ + qemu_irq **irqs_v = g_new(qemu_irq *, n); + memcpy(irqs_v, irqs, sizeof(qemu_irq *) * n); + + dev->irqs = irqs_v; + dev->num_irqs = n; +} + +void platbus_device_set_regions_array(PlatBusDeviceState *dev, int n, + MemoryRegion **regions) +{ + MemoryRegion **regions_v = g_new(MemoryRegion *, n); + memcpy(regions_v, regions, sizeof(MemoryRegion *) * n); + + dev->regions = regions_v; + dev->num_regions = n; +} diff --git a/include/hw/platbus/device.h b/include/hw/platbus/device.h new file mode 100644 index 0000000..f8cbc56 --- /dev/null +++ b/include/hw/platbus/device.h @@ -0,0 +1,102 @@ +/* + * Generic platform bus with dynamic IRQ and IO placement + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Authors: Alexander Graf, <ag...@suse.de> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef QEMU_HW_PLATBUS_DEVICE_H +#define QEMU_HW_PLATBUS_DEVICE_H + +#include "qemu-common.h" +#include "hw/qdev.h" + +#define TYPE_PLATBUS_DEVICE "platbus-device" +#define PLATBUS_DEVICE(obj) OBJECT_CHECK(PlatBusDeviceState, (obj), TYPE_PLATBUS_DEVICE) + +typedef struct PlatBusDeviceState { + /*< private >*/ + + DeviceState parent_obj; + QTAILQ_ENTRY(PlatBusDeviceState) next; + + /* these get set by children through platbus_device_set_xxx() */ + uint32_t num_regions; + MemoryRegion **regions; + uint32_t num_irqs; + qemu_irq **irqs; + + /*< public >*/ + + /* these get set by the user though qdev parameters */ + uint32_t num_plat_region_addrs; + uint64_t *plat_region_addrs; + uint32_t num_plat_irqs; + uint32_t *plat_irqs; +} PlatBusDeviceState; + + +/******** public methods *********/ + + +/** + * platbus_device_set_regions: + * @dev: Parent PlatBusDevice object + * @...: Pointers to properly initialized MemoryRegions + * + * Call this as PlatBusDevice child to tell your parent the location of your + * already initialized memory region objects. + */ +#define platbus_device_set_regions(dev, ...) \ + do { \ + MemoryRegion *regions[] = { __VA_ARGS__ }; \ + platbus_device_set_regions_array(dev, ARRAY_SIZE(regions), regions); \ + } while (0) + +/** + * platbus_device_set_regions_array: + * @dev: Parent PlatBusDevice object + * @n: Number of regions + * @regions: Pointers to properly initialized MemoryRegions + * + * Call this as PlatBusDevice child to tell your parent the location of your + * already initialized memory region objects. + */ +void platbus_device_set_regions_array(PlatBusDeviceState *dev, int n, + MemoryRegion **regions); + +/** + * platbus_device_set_irqs: + * @dev: Parent PlatBusDevice object + * @...: Pointers to qemu_irq fields + * + * Call this as PlatBusDevice child to tell your parent the location of your + * qemu_irq fields. These fields will get populated once the bus is fully + * initialized. + */ +#define platbus_device_set_irqs(dev, ...) \ + do { \ + qemu_irq *irqs[] = { __VA_ARGS__ }; \ + platbus_device_set_irqs_array(dev, ARRAY_SIZE(irqs), irqs); \ + } while (0) + +/** + * platbus_device_set_irqs_array: + * @dev: Parent PlatBusDevice object + * @n: Number of irqs + * @regions: Pointers to qemu_irq fields + * + * Call this as PlatBusDevice child to tell your parent the location of your + * qemu_irq fields. These fields will get populated once the bus is fully + * initialized. + */ +void platbus_device_set_irqs_array(PlatBusDeviceState *dev, int n, + qemu_irq **irqs); + +#endif /* !QEMU_HW_PLATBUS_DEVICE_H */ -- 1.8.1.4