The platform bus is a very simple bus, similar to SysBus. However it has a linear IRQ range that platform devices can attach to. It also provdes mechanisms to dynamically place IO regions.
This patch implements the bus logic itself. Signed-off-by: Alexander Graf <ag...@suse.de> --- hw/platbus/platbus.c | 107 +++++++++++++++++++++++++++++++++++++++++++ include/hw/platbus/platbus.h | 86 ++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 hw/platbus/platbus.c create mode 100644 include/hw/platbus/platbus.h diff --git a/hw/platbus/platbus.c b/hw/platbus/platbus.c new file mode 100644 index 0000000..769eb5d --- /dev/null +++ b/hw/platbus/platbus.c @@ -0,0 +1,107 @@ +/* + * 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. + * + * The platform bus is a very simple bus, similar to SysBus. However it has + * a linear IRQ range that platform devices can attach to. It also provdes + * mechanisms to dynamically place IO regions. + * + * Hence platform bus devices can be easily instantiated using -device from + * the command line. + */ + +#include "qemu-common.h" +#include "hw/platbus/platbus.h" + +int platbus_map_irq(PlatBusState *s, uint32_t *irqn, qemu_irq *irq) +{ + if (*irqn == (uint32_t)PLATBUS_DYNAMIC) { + /* Find the first available IRQ */ + *irqn = find_first_zero_bit(s->used_irqs, PLATBUS_MAX_IRQ); + } + + if ((*irqn >= PLATBUS_MAX_IRQ) || test_and_set_bit(*irqn, s->used_irqs)) { + hw_error("PlatBus: IRQ %d is already allocated or no free IRQ left", + *irqn); + } + + *irq = s->irqs[*irqn]; + + return 0; +} + +void platbus_add_irq(PlatBusState *s, uint32_t irqn) +{ + clear_bit(irqn, s->used_irqs); +} + +int platbus_map_region(PlatBusState *s, uint64_t *addr, MemoryRegion *mem) +{ + uint64_t size = memory_region_size(mem); + uint64_t size_pages = (size + ((1 << 12) - 1)) >> 12; + int page; + int i; + + page = *addr >> 12; + if (*addr == (uint64_t)PLATBUS_DYNAMIC) { + uint64_t size_pages_align; + + /* Align the region to at least its own size granularity */ + if (is_power_of_2(size_pages)) { + size_pages_align = size_pages; + } else { + size_pages_align = pow2floor(size_pages) << 1; + } + + /* Find the first available region that fits */ + page = bitmap_find_next_zero_area(s->used_mem, PLATBUS_HOLE_PAGES, 0, + size_pages, size_pages_align); + + *addr = (uint64_t)page << 12; + } + + if (page >= PLATBUS_HOLE_PAGES || test_bit(page, s->used_mem) || + (find_next_bit(s->used_mem, PLATBUS_HOLE_PAGES, page) < size_pages)) { + hw_error("PlatBus: Memory [%"PRIx64":%"PRIx64" is already allocated or " + "no slot left", *addr, size); + } + + for (i = page; i < (page + size_pages); i++) { + set_bit(i, s->used_mem); + } + + memory_region_add_subregion(&s->mem, *addr, mem); + + return 0; +} + +static void platbus_init(Object *obj) +{ + PlatBusState *s = PLAT_BUS(obj); + + QTAILQ_INIT(&s->devices); + bitmap_fill(s->used_irqs, PLATBUS_MAX_IRQ); + memory_region_init(&s->mem, NULL, "platbus", PLATBUS_HOLE); +} + +static const TypeInfo platbus_type_info = { + .name = TYPE_PLAT_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(PlatBusState), + .instance_init = platbus_init, +}; + +static void platbus_register_types(void) +{ + type_register_static(&platbus_type_info); +} + +type_init(platbus_register_types) diff --git a/include/hw/platbus/platbus.h b/include/hw/platbus/platbus.h new file mode 100644 index 0000000..d260fb8 --- /dev/null +++ b/include/hw/platbus/platbus.h @@ -0,0 +1,86 @@ +/* + * 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_PLATBUS_H +#define QEMU_HW_PLATBUS_PLATBUS_H + +#include "qemu-common.h" +#include "hw/qdev.h" +#include "qemu/bitmap.h" +/* for QDEV_MAX_IRQ */ +#include "hw/sysbus.h" + +#define PLATBUS_MAX_IRQ QDEV_MAX_IRQ +#define PLATBUS_DYNAMIC -1LL +#define PLATBUS_HOLE (128ULL * 1024 * 1024) /* 128 MB */ +#define PLATBUS_HOLE_PAGES (PLATBUS_HOLE >> 12) + +#define TYPE_PLAT_BUS "platbus" +#define PLAT_BUS(obj) OBJECT_CHECK(PlatBusState, (obj), TYPE_PLAT_BUS) + +typedef struct PlatBusState { + /*< private >*/ + BusState parent_obj; + qemu_irq irqs[PLATBUS_MAX_IRQ]; + DECLARE_BITMAP(used_irqs, PLATBUS_MAX_IRQ); + DECLARE_BITMAP(used_mem, PLATBUS_HOLE_PAGES); + /*< public >*/ + + QTAILQ_HEAD(, PlatBusDeviceState) devices; + MemoryRegion mem; +} PlatBusState; + +/** + * platbus_map_region: + * @s: Bus to map the region to + * @addr: Pointer to the offset inside the platform bus address range we want to + * map to. Should be PLATBUS_DYNAMIC if the bus is supposed to give us an + * address. Gets written back with the actually mapped address. + * @mem: Pointer to a properly initialized MemoryRegion which should get mapped + * into the platform bus's address space. Does not get modified. + * + * Tell the platform bus to map a device's memory region into the platform bus's + * address range. + * + * Returns: 0 on success. Negative values on error. + */ +int platbus_map_region(PlatBusState *s, uint64_t *addr, MemoryRegion *mem); + +/** + * platbus_map_region: + * @s: Bus to map the IRQ to + * @irqn: Pointer to the IRQ inside the platform bus IRQ range we want to + * map to. Should be PLATBUS_DYNAMIC if the bus is supposed to give us an + * IRQ number. Gets written back with the actually mapped IRQ. + * @irq: Pointer to a qemu_irq field. Gets populated with a real qemu_irq when + * the mapping gets successfully established. + * + * Tell the platform bus to map a device's IRQ into the platform bus's IRQ range. + * + * Returns: 0 on success. Negative values on error. + */ +int platbus_map_irq(PlatBusState *s, uint32_t *irqn, qemu_irq *irq); + +/** + * platbus_add_irq: + * @s: Bus to map the IRQ to + * @irqn: IRQ number that gets declared valid + * + * Tell the platform bus that one of its internal IRQ lines was successfully + * populated with a usable qemu_irq object. + * + * Returns: 0 on success. Negative values on error. + */ +void platbus_add_irq(PlatBusState *s, uint32_t irqn); + +#endif /* !QEMU_HW_PLATBUS_PLATBUS_H */ -- 1.8.1.4