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 "hw/qdev-properties.h" |
25 | #include "qemu/error-report.h" |
26 | #include "qemu/module.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 | */ |
33 | int 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 | */ |
53 | hwaddr 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 | |
77 | static 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 | */ |
102 | static 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 | } |
107 | |
108 | static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, |
109 | int n) |
110 | { |
111 | int max_irqs = pbus->num_irqs; |
112 | int irqn; |
113 | |
114 | if (sysbus_is_irq_connected(sbdev, n)) { |
115 | /* IRQ is already mapped, nothing to do */ |
116 | return; |
117 | } |
118 | |
119 | irqn = find_first_zero_bit(pbus->used_irqs, max_irqs); |
120 | if (irqn >= max_irqs) { |
121 | error_report("Platform Bus: Can not fit IRQ line" ); |
122 | exit(1); |
123 | } |
124 | |
125 | set_bit(irqn, pbus->used_irqs); |
126 | sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]); |
127 | } |
128 | |
129 | static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, |
130 | int n) |
131 | { |
132 | MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); |
133 | uint64_t size = memory_region_size(sbdev_mr); |
134 | uint64_t alignment = (1ULL << (63 - clz64(size + size - 1))); |
135 | uint64_t off; |
136 | bool found_region = false; |
137 | |
138 | if (memory_region_is_mapped(sbdev_mr)) { |
139 | /* Region is already mapped, nothing to do */ |
140 | return; |
141 | } |
142 | |
143 | /* |
144 | * Look for empty space in the MMIO space that is naturally aligned with |
145 | * the target device's memory region |
146 | */ |
147 | for (off = 0; off < pbus->mmio_size; off += alignment) { |
148 | if (!memory_region_find(&pbus->mmio, off, size).mr) { |
149 | found_region = true; |
150 | break; |
151 | } |
152 | } |
153 | |
154 | if (!found_region) { |
155 | error_report("Platform Bus: Can not fit MMIO region of size %" PRIx64, |
156 | size); |
157 | exit(1); |
158 | } |
159 | |
160 | /* Map the device's region into our Platform Bus MMIO space */ |
161 | memory_region_add_subregion(&pbus->mmio, off, sbdev_mr); |
162 | } |
163 | |
164 | /* |
165 | * Look for unassigned IRQ lines as well as unassociated MMIO regions. |
166 | * Connect them to the platform bus if available. |
167 | */ |
168 | void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev) |
169 | { |
170 | int i; |
171 | |
172 | for (i = 0; sysbus_has_irq(sbdev, i); i++) { |
173 | platform_bus_map_irq(pbus, sbdev, i); |
174 | } |
175 | |
176 | for (i = 0; sysbus_has_mmio(sbdev, i); i++) { |
177 | platform_bus_map_mmio(pbus, sbdev, i); |
178 | } |
179 | } |
180 | |
181 | static void platform_bus_realize(DeviceState *dev, Error **errp) |
182 | { |
183 | PlatformBusDevice *pbus; |
184 | SysBusDevice *d; |
185 | int i; |
186 | |
187 | d = SYS_BUS_DEVICE(dev); |
188 | pbus = PLATFORM_BUS_DEVICE(dev); |
189 | |
190 | memory_region_init(&pbus->mmio, NULL, "platform bus" , pbus->mmio_size); |
191 | sysbus_init_mmio(d, &pbus->mmio); |
192 | |
193 | pbus->used_irqs = bitmap_new(pbus->num_irqs); |
194 | pbus->irqs = g_new0(qemu_irq, pbus->num_irqs); |
195 | for (i = 0; i < pbus->num_irqs; i++) { |
196 | sysbus_init_irq(d, &pbus->irqs[i]); |
197 | } |
198 | |
199 | /* some devices might be initialized before so update used IRQs map */ |
200 | plaform_bus_refresh_irqs(pbus); |
201 | } |
202 | |
203 | static Property platform_bus_properties[] = { |
204 | DEFINE_PROP_UINT32("num_irqs" , PlatformBusDevice, num_irqs, 0), |
205 | DEFINE_PROP_UINT32("mmio_size" , PlatformBusDevice, mmio_size, 0), |
206 | DEFINE_PROP_END_OF_LIST() |
207 | }; |
208 | |
209 | static void platform_bus_class_init(ObjectClass *klass, void *data) |
210 | { |
211 | DeviceClass *dc = DEVICE_CLASS(klass); |
212 | |
213 | dc->realize = platform_bus_realize; |
214 | dc->props = platform_bus_properties; |
215 | } |
216 | |
217 | static const TypeInfo platform_bus_info = { |
218 | .name = TYPE_PLATFORM_BUS_DEVICE, |
219 | .parent = TYPE_SYS_BUS_DEVICE, |
220 | .instance_size = sizeof(PlatformBusDevice), |
221 | .class_init = platform_bus_class_init, |
222 | }; |
223 | |
224 | static void platform_bus_register_types(void) |
225 | { |
226 | type_register_static(&platform_bus_info); |
227 | } |
228 | |
229 | type_init(platform_bus_register_types) |
230 | |