#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" #include "arm-common/pci.h" #include "arm-common/gic.h" /* * An entry in the interrupt-map table looks like: * */ struct of_gic_irq { u32 type, num, flags; } __attribute__((packed)); struct of_interrupt_map_entry { struct of_pci_irq_mask pci_irq_mask; u32 gic_phandle; u32 gic_addr_hi; u32 gic_addr_lo; struct of_gic_irq gic_irq; } __attribute__((packed)); void pci__generate_fdt_nodes(void *fdt, struct kvm *kvm) { enum irqchip_type irqchip = kvm->cfg.arch.irqchip; 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(0), }; /* Configuration Space */ u64 cfg_reg_prop[] = { cpu_to_fdt64(KVM_PCI_CFG_AREA), cpu_to_fdt64(ARM_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(ARM_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(ARM_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))); if (irqchip == IRQCHIP_GICV2M || irqchip == IRQCHIP_GICV3_ITS) _FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_MSI)); /* 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; u32 irq_flags = pci_hdr->irq_type; /* * Avoid adding entries in "interrupt-map" for devices that * will be using advance interrupt mechanisms like MSI or * MSI-X instead of legacy interrupt pins INTA#..INTD# */ if (pin == 0) { dev_hdr = device__next_dev(dev_hdr); continue; } *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), }, .gic_phandle = cpu_to_fdt32(PHANDLE_GIC), .gic_addr_hi = 0, .gic_addr_lo = 0, .gic_irq = { .type = cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI), .num = cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE), .flags = cpu_to_fdt32(irq_flags), }, }; 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)); }