root / hw / spapr_pci.c @ 323abebf
History | View | Annotate | Download (14.4 kB)
1 |
/*
|
---|---|
2 |
* QEMU sPAPR PCI host originated from Uninorth PCI host
|
3 |
*
|
4 |
* Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation.
|
5 |
* Copyright (C) 2011 David Gibson, IBM Corporation.
|
6 |
*
|
7 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8 |
* of this software and associated documentation files (the "Software"), to deal
|
9 |
* in the Software without restriction, including without limitation the rights
|
10 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11 |
* copies of the Software, and to permit persons to whom the Software is
|
12 |
* furnished to do so, subject to the following conditions:
|
13 |
*
|
14 |
* The above copyright notice and this permission notice shall be included in
|
15 |
* all copies or substantial portions of the Software.
|
16 |
*
|
17 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
20 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23 |
* THE SOFTWARE.
|
24 |
*/
|
25 |
#include "hw.h" |
26 |
#include "pci.h" |
27 |
#include "pci_host.h" |
28 |
#include "hw/spapr.h" |
29 |
#include "hw/spapr_pci.h" |
30 |
#include "exec-memory.h" |
31 |
#include <libfdt.h> |
32 |
|
33 |
#include "hw/pci_internals.h" |
34 |
|
35 |
static PCIDevice *find_dev(sPAPREnvironment *spapr,
|
36 |
uint64_t buid, uint32_t config_addr) |
37 |
{ |
38 |
DeviceState *qdev; |
39 |
int devfn = (config_addr >> 8) & 0xFF; |
40 |
sPAPRPHBState *phb; |
41 |
|
42 |
QLIST_FOREACH(phb, &spapr->phbs, list) { |
43 |
if (phb->buid != buid) {
|
44 |
continue;
|
45 |
} |
46 |
|
47 |
QTAILQ_FOREACH(qdev, &phb->host_state.bus->qbus.children, sibling) { |
48 |
PCIDevice *dev = (PCIDevice *)qdev; |
49 |
if (dev->devfn == devfn) {
|
50 |
return dev;
|
51 |
} |
52 |
} |
53 |
} |
54 |
|
55 |
return NULL; |
56 |
} |
57 |
|
58 |
static uint32_t rtas_pci_cfgaddr(uint32_t arg)
|
59 |
{ |
60 |
return ((arg >> 20) & 0xf00) | (arg & 0xff); |
61 |
} |
62 |
|
63 |
static uint32_t rtas_read_pci_config_do(PCIDevice *pci_dev, uint32_t addr,
|
64 |
uint32_t limit, uint32_t len) |
65 |
{ |
66 |
if ((addr + len) <= limit) {
|
67 |
return pci_host_config_read_common(pci_dev, addr, limit, len);
|
68 |
} else {
|
69 |
return ~0x0; |
70 |
} |
71 |
} |
72 |
|
73 |
static void rtas_write_pci_config_do(PCIDevice *pci_dev, uint32_t addr, |
74 |
uint32_t limit, uint32_t val, |
75 |
uint32_t len) |
76 |
{ |
77 |
if ((addr + len) <= limit) {
|
78 |
pci_host_config_write_common(pci_dev, addr, limit, val, len); |
79 |
} |
80 |
} |
81 |
|
82 |
static void rtas_ibm_read_pci_config(sPAPREnvironment *spapr, |
83 |
uint32_t token, uint32_t nargs, |
84 |
target_ulong args, |
85 |
uint32_t nret, target_ulong rets) |
86 |
{ |
87 |
uint32_t val, size, addr; |
88 |
uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); |
89 |
PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0));
|
90 |
|
91 |
if (!dev) {
|
92 |
rtas_st(rets, 0, -1); |
93 |
return;
|
94 |
} |
95 |
size = rtas_ld(args, 3);
|
96 |
addr = rtas_pci_cfgaddr(rtas_ld(args, 0));
|
97 |
val = rtas_read_pci_config_do(dev, addr, pci_config_size(dev), size); |
98 |
rtas_st(rets, 0, 0); |
99 |
rtas_st(rets, 1, val);
|
100 |
} |
101 |
|
102 |
static void rtas_read_pci_config(sPAPREnvironment *spapr, |
103 |
uint32_t token, uint32_t nargs, |
104 |
target_ulong args, |
105 |
uint32_t nret, target_ulong rets) |
106 |
{ |
107 |
uint32_t val, size, addr; |
108 |
PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0)); |
109 |
|
110 |
if (!dev) {
|
111 |
rtas_st(rets, 0, -1); |
112 |
return;
|
113 |
} |
114 |
size = rtas_ld(args, 1);
|
115 |
addr = rtas_pci_cfgaddr(rtas_ld(args, 0));
|
116 |
val = rtas_read_pci_config_do(dev, addr, pci_config_size(dev), size); |
117 |
rtas_st(rets, 0, 0); |
118 |
rtas_st(rets, 1, val);
|
119 |
} |
120 |
|
121 |
static void rtas_ibm_write_pci_config(sPAPREnvironment *spapr, |
122 |
uint32_t token, uint32_t nargs, |
123 |
target_ulong args, |
124 |
uint32_t nret, target_ulong rets) |
125 |
{ |
126 |
uint32_t val, size, addr; |
127 |
uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); |
128 |
PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0));
|
129 |
|
130 |
if (!dev) {
|
131 |
rtas_st(rets, 0, -1); |
132 |
return;
|
133 |
} |
134 |
val = rtas_ld(args, 4);
|
135 |
size = rtas_ld(args, 3);
|
136 |
addr = rtas_pci_cfgaddr(rtas_ld(args, 0));
|
137 |
rtas_write_pci_config_do(dev, addr, pci_config_size(dev), val, size); |
138 |
rtas_st(rets, 0, 0); |
139 |
} |
140 |
|
141 |
static void rtas_write_pci_config(sPAPREnvironment *spapr, |
142 |
uint32_t token, uint32_t nargs, |
143 |
target_ulong args, |
144 |
uint32_t nret, target_ulong rets) |
145 |
{ |
146 |
uint32_t val, size, addr; |
147 |
PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0)); |
148 |
|
149 |
if (!dev) {
|
150 |
rtas_st(rets, 0, -1); |
151 |
return;
|
152 |
} |
153 |
val = rtas_ld(args, 2);
|
154 |
size = rtas_ld(args, 1);
|
155 |
addr = rtas_pci_cfgaddr(rtas_ld(args, 0));
|
156 |
rtas_write_pci_config_do(dev, addr, pci_config_size(dev), val, size); |
157 |
rtas_st(rets, 0, 0); |
158 |
} |
159 |
|
160 |
static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num) |
161 |
{ |
162 |
/*
|
163 |
* Here we need to convert pci_dev + irq_num to some unique value
|
164 |
* which is less than number of IRQs on the specific bus (now it
|
165 |
* is 16). At the moment irq_num == device_id (number of the
|
166 |
* slot?)
|
167 |
* FIXME: we should swizzle in fn and irq_num
|
168 |
*/
|
169 |
return (pci_dev->devfn >> 3) % SPAPR_PCI_NUM_LSI; |
170 |
} |
171 |
|
172 |
static void pci_spapr_set_irq(void *opaque, int irq_num, int level) |
173 |
{ |
174 |
/*
|
175 |
* Here we use the number returned by pci_spapr_map_irq to find a
|
176 |
* corresponding qemu_irq.
|
177 |
*/
|
178 |
sPAPRPHBState *phb = opaque; |
179 |
|
180 |
qemu_set_irq(phb->lsi_table[irq_num].qirq, level); |
181 |
} |
182 |
|
183 |
static int spapr_phb_init(SysBusDevice *s) |
184 |
{ |
185 |
sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); |
186 |
int i;
|
187 |
|
188 |
/* Initialize the LSI table */
|
189 |
for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) { |
190 |
qemu_irq qirq; |
191 |
uint32_t num; |
192 |
|
193 |
qirq = spapr_allocate_irq(0, &num);
|
194 |
if (!qirq) {
|
195 |
return -1; |
196 |
} |
197 |
|
198 |
phb->lsi_table[i].dt_irq = num; |
199 |
phb->lsi_table[i].qirq = qirq; |
200 |
} |
201 |
|
202 |
return 0; |
203 |
} |
204 |
|
205 |
static int spapr_main_pci_host_init(PCIDevice *d) |
206 |
{ |
207 |
return 0; |
208 |
} |
209 |
|
210 |
static void spapr_main_pci_host_class_init(ObjectClass *klass, void *data) |
211 |
{ |
212 |
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
213 |
|
214 |
k->init = spapr_main_pci_host_init; |
215 |
} |
216 |
|
217 |
static TypeInfo spapr_main_pci_host_info = {
|
218 |
.name = "spapr-pci-host-bridge-pci",
|
219 |
.parent = TYPE_PCI_DEVICE, |
220 |
.instance_size = sizeof(PCIDevice),
|
221 |
.class_init = spapr_main_pci_host_class_init, |
222 |
}; |
223 |
|
224 |
static void spapr_phb_class_init(ObjectClass *klass, void *data) |
225 |
{ |
226 |
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); |
227 |
|
228 |
sdc->init = spapr_phb_init; |
229 |
} |
230 |
|
231 |
static TypeInfo spapr_phb_info = {
|
232 |
.name = "spapr-pci-host-bridge",
|
233 |
.parent = TYPE_SYS_BUS_DEVICE, |
234 |
.instance_size = sizeof(sPAPRPHBState),
|
235 |
.class_init = spapr_phb_class_init, |
236 |
}; |
237 |
|
238 |
static void spapr_register_types(void) |
239 |
{ |
240 |
type_register_static(&spapr_phb_info); |
241 |
type_register_static(&spapr_main_pci_host_info); |
242 |
} |
243 |
|
244 |
type_init(spapr_register_types) |
245 |
|
246 |
static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, |
247 |
unsigned size)
|
248 |
{ |
249 |
switch (size) {
|
250 |
case 1: |
251 |
return cpu_inb(addr);
|
252 |
case 2: |
253 |
return cpu_inw(addr);
|
254 |
case 4: |
255 |
return cpu_inl(addr);
|
256 |
} |
257 |
assert(0);
|
258 |
} |
259 |
|
260 |
static void spapr_io_write(void *opaque, target_phys_addr_t addr, |
261 |
uint64_t data, unsigned size)
|
262 |
{ |
263 |
switch (size) {
|
264 |
case 1: |
265 |
cpu_outb(addr, data); |
266 |
return;
|
267 |
case 2: |
268 |
cpu_outw(addr, data); |
269 |
return;
|
270 |
case 4: |
271 |
cpu_outl(addr, data); |
272 |
return;
|
273 |
} |
274 |
assert(0);
|
275 |
} |
276 |
|
277 |
static const MemoryRegionOps spapr_io_ops = { |
278 |
.endianness = DEVICE_LITTLE_ENDIAN, |
279 |
.read = spapr_io_read, |
280 |
.write = spapr_io_write |
281 |
}; |
282 |
|
283 |
void spapr_create_phb(sPAPREnvironment *spapr,
|
284 |
const char *busname, uint64_t buid, |
285 |
uint64_t mem_win_addr, uint64_t mem_win_size, |
286 |
uint64_t io_win_addr) |
287 |
{ |
288 |
DeviceState *dev; |
289 |
SysBusDevice *s; |
290 |
sPAPRPHBState *phb; |
291 |
PCIBus *bus; |
292 |
char namebuf[strlen(busname)+11]; |
293 |
|
294 |
dev = qdev_create(NULL, "spapr-pci-host-bridge"); |
295 |
qdev_init_nofail(dev); |
296 |
s = sysbus_from_qdev(dev); |
297 |
phb = FROM_SYSBUS(sPAPRPHBState, s); |
298 |
|
299 |
phb->mem_win_addr = mem_win_addr; |
300 |
|
301 |
sprintf(namebuf, "%s-mem", busname);
|
302 |
memory_region_init(&phb->memspace, namebuf, INT64_MAX); |
303 |
|
304 |
sprintf(namebuf, "%s-memwindow", busname);
|
305 |
memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace, |
306 |
SPAPR_PCI_MEM_WIN_BUS_OFFSET, mem_win_size); |
307 |
memory_region_add_subregion(get_system_memory(), mem_win_addr, |
308 |
&phb->memwindow); |
309 |
|
310 |
phb->io_win_addr = io_win_addr; |
311 |
|
312 |
/* On ppc, we only have MMIO no specific IO space from the CPU
|
313 |
* perspective. In theory we ought to be able to embed the PCI IO
|
314 |
* memory region direction in the system memory space. However,
|
315 |
* if any of the IO BAR subregions use the old_portio mechanism,
|
316 |
* that won't be processed properly unless accessed from the
|
317 |
* system io address space. This hack to bounce things via
|
318 |
* system_io works around the problem until all the users of
|
319 |
* old_portion are updated */
|
320 |
sprintf(namebuf, "%s-io", busname);
|
321 |
memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); |
322 |
/* FIXME: fix to support multiple PHBs */
|
323 |
memory_region_add_subregion(get_system_io(), 0, &phb->iospace);
|
324 |
|
325 |
sprintf(namebuf, "%s-iowindow", busname);
|
326 |
memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb, |
327 |
namebuf, SPAPR_PCI_IO_WIN_SIZE); |
328 |
memory_region_add_subregion(get_system_memory(), io_win_addr, |
329 |
&phb->iowindow); |
330 |
|
331 |
phb->host_state.bus = bus = pci_register_bus(&phb->busdev.qdev, busname, |
332 |
pci_spapr_set_irq, |
333 |
pci_spapr_map_irq, |
334 |
phb, |
335 |
&phb->memspace, &phb->iospace, |
336 |
PCI_DEVFN(0, 0), |
337 |
SPAPR_PCI_NUM_LSI); |
338 |
|
339 |
spapr_rtas_register("read-pci-config", rtas_read_pci_config);
|
340 |
spapr_rtas_register("write-pci-config", rtas_write_pci_config);
|
341 |
spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config);
|
342 |
spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config);
|
343 |
|
344 |
QLIST_INSERT_HEAD(&spapr->phbs, phb, list); |
345 |
|
346 |
/* pci_bus_set_mem_base(bus, mem_va_start - SPAPR_PCI_MEM_BAR_START); */
|
347 |
} |
348 |
|
349 |
/* Macros to operate with address in OF binding to PCI */
|
350 |
#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) |
351 |
#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ |
352 |
#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ |
353 |
#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ |
354 |
#define b_ss(x) b_x((x), 24, 2) /* the space code */ |
355 |
#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ |
356 |
#define b_ddddd(x) b_x((x), 11, 5) /* device number */ |
357 |
#define b_fff(x) b_x((x), 8, 3) /* function number */ |
358 |
#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ |
359 |
|
360 |
int spapr_populate_pci_devices(sPAPRPHBState *phb,
|
361 |
uint32_t xics_phandle, |
362 |
void *fdt)
|
363 |
{ |
364 |
PCIBus *bus = phb->host_state.bus; |
365 |
int bus_off, i;
|
366 |
char nodename[256]; |
367 |
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; |
368 |
struct {
|
369 |
uint32_t hi; |
370 |
uint64_t child; |
371 |
uint64_t parent; |
372 |
uint64_t size; |
373 |
} __attribute__((packed)) ranges[] = { |
374 |
{ |
375 |
cpu_to_be32(b_ss(1)), cpu_to_be64(0), |
376 |
cpu_to_be64(phb->io_win_addr), |
377 |
cpu_to_be64(memory_region_size(&phb->iospace)), |
378 |
}, |
379 |
{ |
380 |
cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET),
|
381 |
cpu_to_be64(phb->mem_win_addr), |
382 |
cpu_to_be64(memory_region_size(&phb->memwindow)), |
383 |
}, |
384 |
}; |
385 |
uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 };
|
386 |
uint32_t interrupt_map_mask[] = { |
387 |
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, 0x0}; |
388 |
uint32_t interrupt_map[bus->nirq][7];
|
389 |
|
390 |
/* Start populating the FDT */
|
391 |
sprintf(nodename, "pci@%" PRIx64, phb->buid);
|
392 |
bus_off = fdt_add_subnode(fdt, 0, nodename);
|
393 |
if (bus_off < 0) { |
394 |
return bus_off;
|
395 |
} |
396 |
|
397 |
#define _FDT(exp) \
|
398 |
do { \
|
399 |
int ret = (exp); \
|
400 |
if (ret < 0) { \ |
401 |
return ret; \
|
402 |
} \ |
403 |
} while (0) |
404 |
|
405 |
/* Write PHB properties */
|
406 |
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); |
407 |
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); |
408 |
_FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3)); |
409 |
_FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2)); |
410 |
_FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); |
411 |
_FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); |
412 |
_FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); |
413 |
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof(ranges))); |
414 |
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); |
415 |
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); |
416 |
|
417 |
/* Build the interrupt-map, this must matches what is done
|
418 |
* in pci_spapr_map_irq
|
419 |
*/
|
420 |
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map-mask",
|
421 |
&interrupt_map_mask, sizeof(interrupt_map_mask)));
|
422 |
for (i = 0; i < 7; i++) { |
423 |
uint32_t *irqmap = interrupt_map[i]; |
424 |
irqmap[0] = cpu_to_be32(b_ddddd(i)|b_fff(0)); |
425 |
irqmap[1] = 0; |
426 |
irqmap[2] = 0; |
427 |
irqmap[3] = 0; |
428 |
irqmap[4] = cpu_to_be32(xics_phandle);
|
429 |
irqmap[5] = cpu_to_be32(phb->lsi_table[i % SPAPR_PCI_NUM_LSI].dt_irq);
|
430 |
irqmap[6] = cpu_to_be32(0x8); |
431 |
} |
432 |
/* Write interrupt map */
|
433 |
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
|
434 |
7 * sizeof(interrupt_map[0]))); |
435 |
|
436 |
return 0; |
437 |
} |