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,   <agraf@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,   <agraf@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 */
