| 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.1 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/core/platform-bus.h" |
| 24 | #include "hw/core/qdev-properties.h" |
| 25 | #include "qapi/error.h" |
| 26 | #include "qemu/error-report.h" |
| 27 | #include "qemu/module.h" |
| 28 | |
| 29 | |
| 30 | /* |
| 31 | * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if |
| 32 | * the IRQ is not mapped on this Platform bus. |
| 33 | */ |
| 34 | int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev, |
| 35 | int n) |
| 36 | { |
| 37 | qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n); |
| 38 | int i; |
| 39 | |
| 40 | for (i = 0; i < pbus->num_irqs; i++) { |
| 41 | if (pbus->irqs[i] == sbirq) { |
| 42 | return i; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | /* IRQ not mapped on platform bus */ |
| 47 | return -1; |
| 48 | } |
| 49 | |
| 50 | /* |
| 51 | * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or |
| 52 | * -1 if the region is not mapped on this Platform bus. |
| 53 | */ |
| 54 | hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, |
| 55 | int n) |
| 56 | { |
| 57 | MemoryRegion *pbus_mr = &pbus->mmio; |
| 58 | MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); |
| 59 | Object *pbus_mr_obj = OBJECT(pbus_mr); |
| 60 | Object *parent_mr; |
| 61 | |
| 62 | if (!sbdev_mr || !memory_region_is_mapped(sbdev_mr)) { |
| 63 | return -1; |
| 64 | } |
| 65 | |
| 66 | parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", |
| 67 | &error_abort); |
| 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 | |
| 76 | static 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 | */ |
| 101 | static 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 | |
| 107 | static 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 | |
| 128 | static 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 | MemoryRegion *mr = memory_region_find(&pbus->mmio, off, size).mr; |
| 148 | if (!mr) { |
| 149 | found_region = true; |
| 150 | break; |
| 151 | } else { |
| 152 | memory_region_unref(mr); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | if (!found_region) { |
| 157 | error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64, |
| 158 | size); |
| 159 | exit(1); |
| 160 | } |
| 161 | |
| 162 | /* Map the device's region into our Platform Bus MMIO space */ |
| 163 | memory_region_add_subregion(&pbus->mmio, off, sbdev_mr); |
| 164 | } |
| 165 | |
| 166 | /* |
| 167 | * Look for unassigned IRQ lines as well as unassociated MMIO regions. |
| 168 | * Connect them to the platform bus if available. |
| 169 | */ |
| 170 | void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev) |
| 171 | { |
| 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 | |
| 183 | static void platform_bus_realize(DeviceState *dev, Error **errp) |
| 184 | { |
| 185 | PlatformBusDevice *pbus; |
| 186 | SysBusDevice *d; |
| 187 | int i; |
| 188 | |
| 189 | d = SYS_BUS_DEVICE(dev); |
| 190 | pbus = PLATFORM_BUS_DEVICE(dev); |
| 191 | |
| 192 | memory_region_init(&pbus->mmio, OBJECT(dev), "platform bus", |
| 193 | pbus->mmio_size); |
| 194 | sysbus_init_mmio(d, &pbus->mmio); |
| 195 | |
| 196 | pbus->used_irqs = bitmap_new(pbus->num_irqs); |
| 197 | pbus->irqs = g_new0(qemu_irq, pbus->num_irqs); |
| 198 | for (i = 0; i < pbus->num_irqs; i++) { |
| 199 | sysbus_init_irq(d, &pbus->irqs[i]); |
| 200 | } |
| 201 | |
| 202 | /* some devices might be initialized before so update used IRQs map */ |
| 203 | plaform_bus_refresh_irqs(pbus); |
| 204 | } |
| 205 | |
| 206 | static const Property platform_bus_properties[] = { |
| 207 | DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), |
| 208 | DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), |
| 209 | }; |
| 210 | |
| 211 | static void platform_bus_class_init(ObjectClass *klass, const void *data) |
| 212 | { |
| 213 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 214 | |
| 215 | dc->realize = platform_bus_realize; |
| 216 | device_class_set_props(dc, platform_bus_properties); |
| 217 | } |
| 218 | |
| 219 | static const TypeInfo platform_bus_info = { |
| 220 | .name = TYPE_PLATFORM_BUS_DEVICE, |
| 221 | .parent = TYPE_SYS_BUS_DEVICE, |
| 222 | .instance_size = sizeof(PlatformBusDevice), |
| 223 | .class_init = platform_bus_class_init, |
| 224 | }; |
| 225 | |
| 226 | static void platform_bus_register_types(void) |
| 227 | { |
| 228 | type_register_static(&platform_bus_info); |
| 229 | } |
| 230 | |
| 231 | type_init(platform_bus_register_types) |