qemu/hw/core/platform-bus.c
<<
>>
Prefs
   1/*
   2 *  Platform Bus device to support dynamic Sysbus devices
   3 *
   4 * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
   5 *
   6 * Author: Alexander Graf, <agraf@suse.de>
   7 *
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation; either
  11 * version 2 of the License, or (at your option) any later version.
  12 *
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU Lesser General Public
  19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  20 */
  21
  22#include "qemu/osdep.h"
  23#include "hw/platform-bus.h"
  24#include "qemu/error-report.h"
  25#include "sysemu/sysemu.h"
  26
  27
  28/*
  29 * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if
  30 * the IRQ is not mapped on this Platform bus.
  31 */
  32int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev,
  33                          int n)
  34{
  35    qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n);
  36    int i;
  37
  38    for (i = 0; i < pbus->num_irqs; i++) {
  39        if (pbus->irqs[i] == sbirq) {
  40            return i;
  41        }
  42    }
  43
  44    /* IRQ not mapped on platform bus */
  45    return -1;
  46}
  47
  48/*
  49 * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or
  50 * -1 if the region is not mapped on this Platform bus.
  51 */
  52hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev,
  53                                  int n)
  54{
  55    MemoryRegion *pbus_mr = &pbus->mmio;
  56    MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
  57    Object *pbus_mr_obj = OBJECT(pbus_mr);
  58    Object *parent_mr;
  59
  60    if (!memory_region_is_mapped(sbdev_mr)) {
  61        /* Region is not mapped? */
  62        return -1;
  63    }
  64
  65    parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL);
  66
  67    assert(parent_mr);
  68    if (parent_mr != pbus_mr_obj) {
  69        /* MMIO region is not mapped on platform bus */
  70        return -1;
  71    }
  72
  73    return object_property_get_uint(OBJECT(sbdev_mr), "addr", NULL);
  74}
  75
  76static void platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque)
  77{
  78    PlatformBusDevice *pbus = opaque;
  79    qemu_irq sbirq;
  80    int n, i;
  81
  82    for (n = 0; ; n++) {
  83        if (!sysbus_has_irq(sbdev, n)) {
  84            break;
  85        }
  86
  87        sbirq = sysbus_get_connected_irq(sbdev, n);
  88        for (i = 0; i < pbus->num_irqs; i++) {
  89            if (pbus->irqs[i] == sbirq) {
  90                bitmap_set(pbus->used_irqs, i, 1);
  91                break;
  92            }
  93        }
  94    }
  95}
  96
  97/*
  98 * Loop through all sysbus devices and look for unassigned IRQ lines as well as
  99 * unassociated MMIO regions. Connect them to the platform bus if available.
 100 */
 101static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus)
 102{
 103    bitmap_zero(pbus->used_irqs, pbus->num_irqs);
 104    foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus);
 105}
 106
 107static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 108                                 int n)
 109{
 110    int max_irqs = pbus->num_irqs;
 111    int irqn;
 112
 113    if (sysbus_is_irq_connected(sbdev, n)) {
 114        /* IRQ is already mapped, nothing to do */
 115        return;
 116    }
 117
 118    irqn = find_first_zero_bit(pbus->used_irqs, max_irqs);
 119    if (irqn >= max_irqs) {
 120        error_report("Platform Bus: Can not fit IRQ line");
 121        exit(1);
 122    }
 123
 124    set_bit(irqn, pbus->used_irqs);
 125    sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]);
 126}
 127
 128static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 129                                  int n)
 130{
 131    MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
 132    uint64_t size = memory_region_size(sbdev_mr);
 133    uint64_t alignment = (1ULL << (63 - clz64(size + size - 1)));
 134    uint64_t off;
 135    bool found_region = false;
 136
 137    if (memory_region_is_mapped(sbdev_mr)) {
 138        /* Region is already mapped, nothing to do */
 139        return;
 140    }
 141
 142    /*
 143     * Look for empty space in the MMIO space that is naturally aligned with
 144     * the target device's memory region
 145     */
 146    for (off = 0; off < pbus->mmio_size; off += alignment) {
 147        if (!memory_region_find(&pbus->mmio, off, size).mr) {
 148            found_region = true;
 149            break;
 150        }
 151    }
 152
 153    if (!found_region) {
 154        error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64,
 155                     size);
 156        exit(1);
 157    }
 158
 159    /* Map the device's region into our Platform Bus MMIO space */
 160    memory_region_add_subregion(&pbus->mmio, off, sbdev_mr);
 161}
 162
 163/*
 164 * Look for unassigned IRQ lines as well as unassociated MMIO regions.
 165 * Connect them to the platform bus if available.
 166 */
 167void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev)
 168{
 169    int i;
 170
 171    for (i = 0; sysbus_has_irq(sbdev, i); i++) {
 172        platform_bus_map_irq(pbus, sbdev, i);
 173    }
 174
 175    for (i = 0; sysbus_has_mmio(sbdev, i); i++) {
 176        platform_bus_map_mmio(pbus, sbdev, i);
 177    }
 178}
 179
 180static void platform_bus_realize(DeviceState *dev, Error **errp)
 181{
 182    PlatformBusDevice *pbus;
 183    SysBusDevice *d;
 184    int i;
 185
 186    d = SYS_BUS_DEVICE(dev);
 187    pbus = PLATFORM_BUS_DEVICE(dev);
 188
 189    memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size);
 190    sysbus_init_mmio(d, &pbus->mmio);
 191
 192    pbus->used_irqs = bitmap_new(pbus->num_irqs);
 193    pbus->irqs = g_new0(qemu_irq, pbus->num_irqs);
 194    for (i = 0; i < pbus->num_irqs; i++) {
 195        sysbus_init_irq(d, &pbus->irqs[i]);
 196    }
 197
 198    /* some devices might be initialized before so update used IRQs map */
 199    plaform_bus_refresh_irqs(pbus);
 200}
 201
 202static Property platform_bus_properties[] = {
 203    DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0),
 204    DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0),
 205    DEFINE_PROP_END_OF_LIST()
 206};
 207
 208static void platform_bus_class_init(ObjectClass *klass, void *data)
 209{
 210    DeviceClass *dc = DEVICE_CLASS(klass);
 211
 212    dc->realize = platform_bus_realize;
 213    dc->props = platform_bus_properties;
 214}
 215
 216static const TypeInfo platform_bus_info = {
 217    .name          = TYPE_PLATFORM_BUS_DEVICE,
 218    .parent        = TYPE_SYS_BUS_DEVICE,
 219    .instance_size = sizeof(PlatformBusDevice),
 220    .class_init    = platform_bus_class_init,
 221};
 222
 223static void platform_bus_register_types(void)
 224{
 225    type_register_static(&platform_bus_info);
 226}
 227
 228type_init(platform_bus_register_types)
 229