xref: /kvmtool/arm/pci.c (revision 7a60af05c183eb25deb1c5a333c232c42eea5944)
1 #include "kvm/devices.h"
2 #include "kvm/fdt.h"
3 #include "kvm/kvm.h"
4 #include "kvm/of_pci.h"
5 #include "kvm/pci.h"
6 #include "kvm/util.h"
7 
8 #include "arm-common/pci.h"
9 
10 /*
11  * An entry in the interrupt-map table looks like:
12  * <pci unit address> <pci interrupt pin> <gic phandle> <gic interrupt>
13  */
14 
15 struct of_gic_irq {
16 	u32 type, num, flags;
17 } __attribute__((packed));
18 
19 struct of_interrupt_map_entry {
20 	struct of_pci_irq_mask		pci_irq_mask;
21 	u32				gic_phandle;
22 	u32				gic_addr_hi;
23 	u32				gic_addr_lo;
24 	struct of_gic_irq		gic_irq;
25 } __attribute__((packed));
26 
27 void pci__generate_fdt_nodes(void *fdt)
28 {
29 	struct device_header *dev_hdr;
30 	struct of_interrupt_map_entry irq_map[OF_PCI_IRQ_MAP_MAX];
31 	unsigned nentries = 0;
32 	/* Bus range */
33 	u32 bus_range[] = { cpu_to_fdt32(0), cpu_to_fdt32(0), };
34 	/* Configuration Space */
35 	u64 cfg_reg_prop[] = { cpu_to_fdt64(KVM_PCI_CFG_AREA),
36 			       cpu_to_fdt64(ARM_PCI_CFG_SIZE), };
37 	/* Describe the memory ranges */
38 	struct of_pci_ranges_entry ranges[] = {
39 		{
40 			.pci_addr = {
41 				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_IO)),
42 				.mid	= 0,
43 				.lo	= 0,
44 			},
45 			.cpu_addr	= cpu_to_fdt64(KVM_IOPORT_AREA),
46 			.length		= cpu_to_fdt64(ARM_IOPORT_SIZE),
47 		},
48 		{
49 			.pci_addr = {
50 				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_M32)),
51 				.mid	= cpu_to_fdt32(KVM_PCI_MMIO_AREA >> 32),
52 				.lo	= cpu_to_fdt32(KVM_PCI_MMIO_AREA),
53 			},
54 			.cpu_addr	= cpu_to_fdt64(KVM_PCI_MMIO_AREA),
55 			.length		= cpu_to_fdt64(ARM_PCI_MMIO_SIZE),
56 		},
57 	};
58 
59 	/* Boilerplate PCI properties */
60 	_FDT(fdt_begin_node(fdt, "pci"));
61 	_FDT(fdt_property_string(fdt, "device_type", "pci"));
62 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x3));
63 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
64 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0x1));
65 	_FDT(fdt_property_string(fdt, "compatible", "pci-host-ecam-generic"));
66 	_FDT(fdt_property(fdt, "dma-coherent", NULL, 0));
67 
68 	_FDT(fdt_property(fdt, "bus-range", bus_range, sizeof(bus_range)));
69 	_FDT(fdt_property(fdt, "reg", &cfg_reg_prop, sizeof(cfg_reg_prop)));
70 	_FDT(fdt_property(fdt, "ranges", ranges, sizeof(ranges)));
71 	_FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_MSI));
72 
73 	/* Generate the interrupt map ... */
74 	dev_hdr = device__first_dev(DEVICE_BUS_PCI);
75 	while (dev_hdr && nentries < ARRAY_SIZE(irq_map)) {
76 		struct of_interrupt_map_entry *entry = &irq_map[nentries];
77 		struct pci_device_header *pci_hdr = dev_hdr->data;
78 		u8 dev_num = dev_hdr->dev_num;
79 		u8 pin = pci_hdr->irq_pin;
80 		u8 irq = pci_hdr->irq_line;
81 		u32 irq_flags = pci_hdr->irq_type;
82 
83 		/*
84 		 * Avoid adding entries in "interrupt-map" for devices that
85 		 * will be using advance interrupt mechanisms like MSI or
86 		 * MSI-X instead of legacy interrupt pins INTA#..INTD#
87 		 */
88 		if (pin == 0) {
89 			dev_hdr = device__next_dev(dev_hdr);
90 			continue;
91 		}
92 
93 		*entry = (struct of_interrupt_map_entry) {
94 			.pci_irq_mask = {
95 				.pci_addr = {
96 					.hi	= cpu_to_fdt32(of_pci_b_ddddd(dev_num)),
97 					.mid	= 0,
98 					.lo	= 0,
99 				},
100 				.pci_pin	= cpu_to_fdt32(pin),
101 			},
102 			.gic_phandle	= cpu_to_fdt32(PHANDLE_GIC),
103 			.gic_addr_hi	= 0,
104 			.gic_addr_lo	= 0,
105 			.gic_irq = {
106 				.type	= cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
107 				.num	= cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE),
108 				.flags	= cpu_to_fdt32(irq_flags),
109 			},
110 		};
111 
112 		nentries++;
113 		dev_hdr = device__next_dev(dev_hdr);
114 	}
115 
116 	_FDT(fdt_property(fdt, "interrupt-map", irq_map,
117 			  sizeof(struct of_interrupt_map_entry) * nentries));
118 
119 	/* ... and the corresponding mask. */
120 	if (nentries) {
121 		struct of_pci_irq_mask irq_mask = {
122 			.pci_addr = {
123 				.hi	= cpu_to_fdt32(of_pci_b_ddddd(-1)),
124 				.mid	= 0,
125 				.lo	= 0,
126 			},
127 			.pci_pin	= cpu_to_fdt32(7),
128 		};
129 
130 		_FDT(fdt_property(fdt, "interrupt-map-mask", &irq_mask,
131 				  sizeof(irq_mask)));
132 	}
133 
134 	_FDT(fdt_end_node(fdt));
135 }
136