aboutsummaryrefslogtreecommitdiffstats
path: root/riscv/pci.c
blob: 604fd20d34a6f07d4fabf391d3694f1157a95458 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "kvm/devices.h"
#include "kvm/fdt.h"
#include "kvm/kvm.h"
#include "kvm/of_pci.h"
#include "kvm/pci.h"
#include "kvm/util.h"

/*
 * An entry in the interrupt-map table looks like:
 * <pci unit address> <pci interrupt pin> <plic phandle> <plic interrupt>
 */

struct of_interrupt_map_entry {
	struct of_pci_irq_mask		pci_irq_mask;
	u32				plic_phandle;
	u32				plic_irq;
} __attribute__((packed));

void pci__generate_fdt_nodes(void *fdt)
{
	struct device_header *dev_hdr;
	struct of_interrupt_map_entry irq_map[OF_PCI_IRQ_MAP_MAX];
	unsigned nentries = 0;
	/* Bus range */
	u32 bus_range[] = { cpu_to_fdt32(0), cpu_to_fdt32(1), };
	/* Configuration Space */
	u64 cfg_reg_prop[] = { cpu_to_fdt64(KVM_PCI_CFG_AREA),
			       cpu_to_fdt64(RISCV_PCI_CFG_SIZE), };
	/* Describe the memory ranges */
	struct of_pci_ranges_entry ranges[] = {
		{
			.pci_addr = {
				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_IO)),
				.mid	= 0,
				.lo	= 0,
			},
			.cpu_addr	= cpu_to_fdt64(KVM_IOPORT_AREA),
			.length		= cpu_to_fdt64(RISCV_IOPORT_SIZE),
		},
		{
			.pci_addr = {
				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_M32)),
				.mid	= cpu_to_fdt32(KVM_PCI_MMIO_AREA >> 32),
				.lo	= cpu_to_fdt32(KVM_PCI_MMIO_AREA),
			},
			.cpu_addr	= cpu_to_fdt64(KVM_PCI_MMIO_AREA),
			.length		= cpu_to_fdt64(RISCV_PCI_MMIO_SIZE),
		},
	};

	/* Boilerplate PCI properties */
	_FDT(fdt_begin_node(fdt, "pci"));
	_FDT(fdt_property_string(fdt, "device_type", "pci"));
	_FDT(fdt_property_cell(fdt, "#address-cells", 0x3));
	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0x1));
	_FDT(fdt_property_string(fdt, "compatible", "pci-host-ecam-generic"));
	_FDT(fdt_property(fdt, "dma-coherent", NULL, 0));

	_FDT(fdt_property(fdt, "bus-range", bus_range, sizeof(bus_range)));
	_FDT(fdt_property(fdt, "reg", &cfg_reg_prop, sizeof(cfg_reg_prop)));
	_FDT(fdt_property(fdt, "ranges", ranges, sizeof(ranges)));

	/* Generate the interrupt map ... */
	dev_hdr = device__first_dev(DEVICE_BUS_PCI);
	while (dev_hdr && nentries < ARRAY_SIZE(irq_map)) {
		struct of_interrupt_map_entry *entry = &irq_map[nentries];
		struct pci_device_header *pci_hdr = dev_hdr->data;
		u8 dev_num = dev_hdr->dev_num;
		u8 pin = pci_hdr->irq_pin;
		u8 irq = pci_hdr->irq_line;

		*entry = (struct of_interrupt_map_entry) {
			.pci_irq_mask = {
				.pci_addr = {
					.hi	= cpu_to_fdt32(of_pci_b_ddddd(dev_num)),
					.mid	= 0,
					.lo	= 0,
				},
				.pci_pin	= cpu_to_fdt32(pin),
			},
			.plic_phandle	= cpu_to_fdt32(PHANDLE_PLIC),
			.plic_irq	= cpu_to_fdt32(irq),
		};

		nentries++;
		dev_hdr = device__next_dev(dev_hdr);
	}

	_FDT(fdt_property(fdt, "interrupt-map", irq_map,
			  sizeof(struct of_interrupt_map_entry) * nentries));

	/* ... and the corresponding mask. */
	if (nentries) {
		struct of_pci_irq_mask irq_mask = {
			.pci_addr = {
				.hi	= cpu_to_fdt32(of_pci_b_ddddd(-1)),
				.mid	= 0,
				.lo	= 0,
			},
			.pci_pin	= cpu_to_fdt32(7),
		};

		_FDT(fdt_property(fdt, "interrupt-map-mask", &irq_mask,
				  sizeof(irq_mask)));
	}

	_FDT(fdt_end_node(fdt));
}