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 */ 32 int 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 */ 52 hwaddr 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 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 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 */ 167 void 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 180 static 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 202 static 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 208 static 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 216 static 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 223 static void platform_bus_register_types(void) 224 { 225 type_register_static(&platform_bus_info); 226 } 227 228 type_init(platform_bus_register_types) 229