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_uint(OBJECT(sbdev_mr), "addr", NULL);
  75}
  76
  77static void 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
  98/*
  99 * Loop through all sysbus devices and look for unassigned IRQ lines as well as
 100 * unassociated MMIO regions. Connect them to the platform bus if available.
 101 */
 102static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus)
 103{
 104    bitmap_zero(pbus->used_irqs, pbus->num_irqs);
 105    foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus);
 106    pbus->done_gathering = true;
 107}
 108
 109static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 110                                 int n)
 111{
 112    int max_irqs = pbus->num_irqs;
 113    int irqn;
 114
 115    if (sysbus_is_irq_connected(sbdev, n)) {
 116        /* IRQ is already mapped, nothing to do */
 117        return;
 118    }
 119
 120    irqn = find_first_zero_bit(pbus->used_irqs, max_irqs);
 121    if (irqn >= max_irqs) {
 122        error_report("Platform Bus: Can not fit IRQ line");
 123        exit(1);
 124    }
 125
 126    set_bit(irqn, pbus->used_irqs);
 127    sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]);
 128}
 129
 130static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
 131                                  int n)
 132{
 133    MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
 134    uint64_t size = memory_region_size(sbdev_mr);
 135    uint64_t alignment = (1ULL << (63 - clz64(size + size - 1)));
 136    uint64_t off;
 137    bool found_region = false;
 138
 139    if (memory_region_is_mapped(sbdev_mr)) {
 140        /* Region is already mapped, nothing to do */
 141        return;
 142    }
 143
 144    /*
 145     * Look for empty space in the MMIO space that is naturally aligned with
 146     * the target device's memory region
 147     */
 148    for (off = 0; off < pbus->mmio_size; off += alignment) {
 149        if (!memory_region_find(&pbus->mmio, off, size).mr) {
 150            found_region = true;
 151            break;
 152        }
 153    }
 154
 155    if (!found_region) {
 156        error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64,
 157                     size);
 158        exit(1);
 159    }
 160
 161    /* Map the device's region into our Platform Bus MMIO space */
 162    memory_region_add_subregion(&pbus->mmio, off, sbdev_mr);
 163}
 164
 165/*
 166 * For each sysbus device, look for unassigned IRQ lines as well as
 167 * unassociated MMIO regions. Connect them to the platform bus if available.
 168 */
 169static void link_sysbus_device(SysBusDevice *sbdev, void *opaque)
 170{
 171    PlatformBusDevice *pbus = opaque;
 172    int i;
 173
 174    for (i = 0; sysbus_has_irq(sbdev, i); i++) {
 175        platform_bus_map_irq(pbus, sbdev, i);
 176    }
 177
 178    for (i = 0; sysbus_has_mmio(sbdev, i); i++) {
 179        platform_bus_map_mmio(pbus, sbdev, i);
 180    }
 181}
 182
 183static void platform_bus_init_notify(Notifier *notifier, void *data)
 184{
 185    PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier);
 186
 187    /*
 188     * Generate a bitmap of used IRQ lines, as the user might have specified
 189     * them on the command line.
 190     */
 191    plaform_bus_refresh_irqs(pb);
 192
 193    foreach_dynamic_sysbus_device(link_sysbus_device, pb);
 194}
 195
 196static void platform_bus_realize(DeviceState *dev, Error **errp)
 197{
 198    PlatformBusDevice *pbus;
 199    SysBusDevice *d;
 200    int i;
 201
 202    d = SYS_BUS_DEVICE(dev);
 203    pbus = PLATFORM_BUS_DEVICE(dev);
 204
 205    memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size);
 206    sysbus_init_mmio(d, &pbus->mmio);
 207
 208    pbus->used_irqs = bitmap_new(pbus->num_irqs);
 209    pbus->irqs = g_new0(qemu_irq, pbus->num_irqs);
 210    for (i = 0; i < pbus->num_irqs; i++) {
 211        sysbus_init_irq(d, &pbus->irqs[i]);
 212    }
 213
 214    /*
 215     * Register notifier that allows us to gather dangling devices once the
 216     * machine is completely assembled
 217     */
 218    pbus->notifier.notify = platform_bus_init_notify;
 219    qemu_add_machine_init_done_notifier(&pbus->notifier);
 220}
 221
 222static Property platform_bus_properties[] = {
 223    DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0),
 224    DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0),
 225    DEFINE_PROP_END_OF_LIST()
 226};
 227
 228static void platform_bus_class_init(ObjectClass *klass, void *data)
 229{
 230    DeviceClass *dc = DEVICE_CLASS(klass);
 231
 232    dc->realize = platform_bus_realize;
 233    dc->props = platform_bus_properties;
 234}
 235
 236static const TypeInfo platform_bus_info = {
 237    .name          = TYPE_PLATFORM_BUS_DEVICE,
 238    .parent        = TYPE_SYS_BUS_DEVICE,
 239    .instance_size = sizeof(PlatformBusDevice),
 240    .class_init    = platform_bus_class_init,
 241};
 242
 243static void platform_bus_register_types(void)
 244{
 245    type_register_static(&platform_bus_info);
 246}
 247
 248type_init(platform_bus_register_types)
 249