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 "exec/address-spaces.h"
  25#include "qemu/error-report.h"
  26#include "sysemu/sysemu.h"
  27
  28
  29/*
  30 * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if
  31 * the IRQ is not mapped on this Platform bus.
  32 */
  33int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev,
  34                          int n)
  35{
  36    qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n);
  37    int i;
  38
  39    for (i = 0; i < pbus->num_irqs; i++) {
  40        if (pbus->irqs[i] == sbirq) {
  41            return i;
  42        }
  43    }
  44
  45    /* IRQ not mapped on platform bus */
  46    return -1;
  47}
  48
  49/*
  50 * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or
  51 * -1 if the region is not mapped on this Platform bus.
  52 */
  53hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev,
  54                                  int n)
  55{
  56    MemoryRegion *pbus_mr = &pbus->mmio;
  57    MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
  58    Object *pbus_mr_obj = OBJECT(pbus_mr);
  59    Object *parent_mr;
  60
  61    if (!memory_region_is_mapped(sbdev_mr)) {
  62        /* Region is not mapped? */
  63        return -1;
  64    }
  65
  66    parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL);
  67
  68    assert(parent_mr);
  69    if (parent_mr != pbus_mr_obj) {
  70        /* MMIO region is not mapped on platform bus */
  71        return -1;
  72    }
  73
  74    return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL);
  75}
  76
  77static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque)
  78{
  79    PlatformBusDevice *pbus = opaque;
  80    qemu_irq sbirq;
  81    int n, i;
  82
  83    for (n = 0; ; n++) {
  84        if (!sysbus_has_irq(sbdev, n)) {
  85            break;
  86        }
  87
  88        sbirq = sysbus_get_connected_irq(sbdev, n);
  89        for (i = 0; i < pbus->num_irqs; i++) {
  90            if (pbus->irqs[i] == sbirq) {
  91                bitmap_set(pbus->used_irqs, i, 1);
  92                break;
  93            }
  94        }
  95    }
  96
  97    return 0;
  98}
  99
 100/*
 101 * Loop through all sysbus devices and look for unassigned IRQ lines as well as
 102 * unassociated MMIO regions. Connect them to the platform bus if available.
 103 */
 104static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus)
 105{
 106    bitmap_zero(pbus->used_irqs, pbus->num_irqs);
 107    foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus);
 108    pbus->done_gathering = true;
 109}
 110
 111static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 112                                 int n)
 113{
 114    int max_irqs = pbus->num_irqs;
 115    int irqn;
 116
 117    if (sysbus_is_irq_connected(sbdev, n)) {
 118        /* IRQ is already mapped, nothing to do */
 119        return;
 120    }
 121
 122    irqn = find_first_zero_bit(pbus->used_irqs, max_irqs);
 123    if (irqn >= max_irqs) {
 124        error_report("Platform Bus: Can not fit IRQ line");
 125        exit(1);
 126    }
 127
 128    set_bit(irqn, pbus->used_irqs);
 129    sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]);
 130}
 131
 132static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 133                                  int n)
 134{
 135    MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
 136    uint64_t size = memory_region_size(sbdev_mr);
 137    uint64_t alignment = (1ULL << (63 - clz64(size + size - 1)));
 138    uint64_t off;
 139    bool found_region = false;
 140
 141    if (memory_region_is_mapped(sbdev_mr)) {
 142        /* Region is already mapped, nothing to do */
 143        return;
 144    }
 145
 146    /*
 147     * Look for empty space in the MMIO space that is naturally aligned with
 148     * the target device's memory region
 149     */
 150    for (off = 0; off < pbus->mmio_size; off += alignment) {
 151        if (!memory_region_find(&pbus->mmio, off, size).mr) {
 152            found_region = true;
 153            break;
 154        }
 155    }
 156
 157    if (!found_region) {
 158        error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64,
 159                     size);
 160        exit(1);
 161    }
 162
 163    /* Map the device's region into our Platform Bus MMIO space */
 164    memory_region_add_subregion(&pbus->mmio, off, sbdev_mr);
 165}
 166
 167/*
 168 * For each sysbus device, look for unassigned IRQ lines as well as
 169 * unassociated MMIO regions. Connect them to the platform bus if available.
 170 */
 171static int link_sysbus_device(SysBusDevice *sbdev, void *opaque)
 172{
 173    PlatformBusDevice *pbus = opaque;
 174    int i;
 175
 176    for (i = 0; sysbus_has_irq(sbdev, i); i++) {
 177        platform_bus_map_irq(pbus, sbdev, i);
 178    }
 179
 180    for (i = 0; sysbus_has_mmio(sbdev, i); i++) {
 181        platform_bus_map_mmio(pbus, sbdev, i);
 182    }
 183
 184    return 0;
 185}
 186
 187static void platform_bus_init_notify(Notifier *notifier, void *data)
 188{
 189    PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier);
 190
 191    /*
 192     * Generate a bitmap of used IRQ lines, as the user might have specified
 193     * them on the command line.
 194     */
 195    plaform_bus_refresh_irqs(pb);
 196
 197    foreach_dynamic_sysbus_device(link_sysbus_device, pb);
 198}
 199
 200static void platform_bus_realize(DeviceState *dev, Error **errp)
 201{
 202    PlatformBusDevice *pbus;
 203    SysBusDevice *d;
 204    int i;
 205
 206    d = SYS_BUS_DEVICE(dev);
 207    pbus = PLATFORM_BUS_DEVICE(dev);
 208
 209    memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size);
 210    sysbus_init_mmio(d, &pbus->mmio);
 211
 212    pbus->used_irqs = bitmap_new(pbus->num_irqs);
 213    pbus->irqs = g_new0(qemu_irq, pbus->num_irqs);
 214    for (i = 0; i < pbus->num_irqs; i++) {
 215        sysbus_init_irq(d, &pbus->irqs[i]);
 216    }
 217
 218    /*
 219     * Register notifier that allows us to gather dangling devices once the
 220     * machine is completely assembled
 221     */
 222    pbus->notifier.notify = platform_bus_init_notify;
 223    qemu_add_machine_init_done_notifier(&pbus->notifier);
 224}
 225
 226static Property platform_bus_properties[] = {
 227    DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0),
 228    DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0),
 229    DEFINE_PROP_END_OF_LIST()
 230};
 231
 232static void platform_bus_class_init(ObjectClass *klass, void *data)
 233{
 234    DeviceClass *dc = DEVICE_CLASS(klass);
 235
 236    dc->realize = platform_bus_realize;
 237    dc->props = platform_bus_properties;
 238}
 239
 240static const TypeInfo platform_bus_info = {
 241    .name          = TYPE_PLATFORM_BUS_DEVICE,
 242    .parent        = TYPE_SYS_BUS_DEVICE,
 243    .instance_size = sizeof(PlatformBusDevice),
 244    .class_init    = platform_bus_class_init,
 245};
 246
 247static void platform_bus_register_types(void)
 248{
 249    type_register_static(&platform_bus_info);
 250}
 251
 252type_init(platform_bus_register_types)
 253