From: Nicholas Piggin <[email protected]> tt-atlantis is likely to use a generic ECAM compatible PCIe memory map, so gpex is not far off the OS programming model
Signed-off-by: Nicholas Piggin <[email protected]> Signed-off-by: Joel Stanley <[email protected]> --- include/hw/riscv/tt_atlantis.h | 2 + hw/riscv/tt_atlantis.c | 218 +++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 2 + 3 files changed, 222 insertions(+) diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h index 6b0777b42c32..10b06894aeca 100644 --- a/include/hw/riscv/tt_atlantis.h +++ b/include/hw/riscv/tt_atlantis.h @@ -29,6 +29,7 @@ struct TTAtlantisState { RISCVHartArrayState soc; DeviceState *irqchip; + GPEXHost gpex_host; int fdt_size; int aia_guests; /* TODO: This should be hard coded once known */ @@ -41,6 +42,7 @@ enum { TT_ATL_UART2_IRQ = 40, TT_ATL_UART3_IRQ = 41, TT_ATL_UART4_IRQ = 42, + TT_ATL_PCIE0_INTA_IRQ = 96, }; enum { diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index 6bde78839a9f..fa6e4f42a12d 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -27,6 +27,7 @@ #include "hw/intc/riscv_aclint.h" #include "hw/intc/riscv_aplic.h" #include "hw/misc/pvpanic.h" +#include "hw/pci-host/gpex.h" #include "system/system.h" #include "system/device_tree.h" @@ -73,6 +74,9 @@ static const MemMapEntry tt_atlantis_memmap[] = { [TT_ATL_PCIE_ECAM1] = { 0x01120000000, 0x10000000 }, [TT_ATL_PCIE_ECAM2] = { 0x01130000000, 0x10000000 }, [TT_ATL_PCIE_MMIO0] = { 0x10000000000, 0x10000000000 }, + [TT_ATL_PCIE_PIO0] = { 0x10000000000, 0x10000 }, /* qemu only */ + [TT_ATL_PCIE_MMIO0_32] = { 0x10004000000, 0x4000000 }, /* qemu only */ + [TT_ATL_PCIE_MMIO0_64] = { 0x10010000000, 0x0fff0000000 }, /* qemu only */ [TT_ATL_PCIE_MMIO1] = { 0x20000000000, 0x10000000000 }, [TT_ATL_PCIE_MMIO2] = { 0x30000000000, 0x10000000000 }, }; @@ -83,6 +87,59 @@ static uint32_t next_phandle(void) return phandle++; } +static void create_pcie_irq_map(void *fdt, char *nodename, int legacy_irq, + uint32_t irqchip_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * + FDT_MAX_INT_MAP_WIDTH] = {}; + uint32_t *irq_map = full_irq_map; + + /* + * This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + for (dev = 0; dev < PCI_NUM_PINS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = legacy_irq + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i] = cpu_to_be32(devfn << 8); + i += FDT_PCI_ADDR_CELLS; + + /* Fill PCI Interrupt cells */ + irq_map[i] = cpu_to_be32(pin + 1); + i += FDT_PCI_INT_CELLS; + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(irqchip_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + irq_map[i++] = cpu_to_be32(0x4); + + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; + } + } + + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, + PCI_NUM_PINS * PCI_NUM_PINS * + irq_map_stride * sizeof(uint32_t)); + + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles) { uint32_t cpu_phandle; @@ -323,6 +380,54 @@ static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap, IRQ_S_EXT, s->soc.num_harts); } +static void create_fdt_pcie(void *fdt, + const MemMapEntry *mem_ecam, + const MemMapEntry *mem_pio, + const MemMapEntry *mem_mmio32, + const MemMapEntry *mem_mmio64, + int legacy_irq, + uint32_t aplic_s_phandle, + uint32_t imsic_s_phandle) +{ + g_autofree char *name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + mem_ecam->base); + + qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS); + qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2); + qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(fdt, name, "device_type", "pci"); + qemu_fdt_setprop_cells(fdt, name, "bus-range", 0, + mem_ecam->size / PCIE_MMCFG_SIZE_MIN - 1); + qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cell(fdt, name, "msi-parent", imsic_s_phandle); + + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_ecam->base, + 2, mem_ecam->size); + if (!(mem_mmio32->base & 0xffffffffUL)) { + /* XXX: this is a silly hack because it would collide with PIO */ + error_report("mmio32 base must not be 0 mod 2^32"); + exit(1); + } + uint32_t flags = FDT_PCI_RANGE_MMIO_64BIT | FDT_PCI_RANGE_PREFETCHABLE; + qemu_fdt_setprop_sized_cells(fdt, name, "ranges", + 1, FDT_PCI_RANGE_IOPORT, + 2, 0x0, + 2, mem_pio->base, + 2, mem_pio->size, + 1, FDT_PCI_RANGE_MMIO, + 2, (mem_mmio32->base & 0xffffffffUL), + 2, mem_mmio32->base, + 2, mem_mmio32->size, + 1, flags, + 2, mem_mmio64->base, + 2, mem_mmio64->base, + 2, mem_mmio64->size); + + create_pcie_irq_map(fdt, name, legacy_irq, aplic_s_phandle); +} + static void create_fdt_reset(void *fdt, const MemMapEntry *mem) { uint32_t syscon_phandle = next_phandle(); @@ -388,6 +493,14 @@ static void finalize_fdt(TTAtlantisState *s) * aplic_s_phandle); */ + create_fdt_pcie(fdt, + &s->memmap[TT_ATL_PCIE_ECAM0], + &s->memmap[TT_ATL_PCIE_PIO0], + &s->memmap[TT_ATL_PCIE_MMIO0_32], + &s->memmap[TT_ATL_PCIE_MMIO0_64], + TT_ATL_PCIE0_INTA_IRQ, + aplic_s_phandle, imsic_s_phandle); + create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]); create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ, @@ -420,6 +533,20 @@ static void create_fdt(TTAtlantisState *s) qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + /* + * The "/soc/pci@..." node is needed for PCIE hotplugs + * that might happen before finalize_fdt(). + */ + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM0].base); + qemu_fdt_add_subnode(fdt, name); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM1].base); + qemu_fdt_add_subnode(fdt, name); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM2].base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_add_subnode(fdt, "/chosen"); /* Pass seed to RNG */ @@ -432,6 +559,93 @@ static void create_fdt(TTAtlantisState *s) create_fdt_pmu(s); } +static void gpex_pcie_init_one(TTAtlantisState *s, GPEXHost *gpex_host, + MemoryRegion *mr, + const MemMapEntry *mem_ecam, + const MemMapEntry *mem_pio, + const MemMapEntry *mem_mmio32, + const MemMapEntry *mem_mmio64, + int legacy_irq) +{ + DeviceState *dev; + Object *obj; + MemoryRegion *ecam_alias, *ecam_reg; + MemoryRegion *mmio32_alias, *mmio64_alias, *mmio_reg; + hwaddr ecam_base = mem_ecam->base; + hwaddr ecam_size = mem_ecam->size; + hwaddr pio_base = mem_pio->base; + hwaddr pio_size = mem_pio->size; + hwaddr mmio32_base = mem_mmio32->base; + hwaddr mmio32_size = mem_mmio32->size; + hwaddr mmio64_base = mem_mmio64->base; + hwaddr mmio64_size = mem_mmio64->size; + qemu_irq irq; + char name[16]; + int i; + + snprintf(name, sizeof(name), "pcie"); + object_initialize_child(OBJECT(s), name, gpex_host, TYPE_GPEX_HOST); + dev = DEVICE(gpex_host); + obj = OBJECT(dev); + + object_property_set_uint(obj, PCI_HOST_ECAM_BASE, ecam_base, &error_abort); + object_property_set_int(obj, PCI_HOST_ECAM_SIZE, ecam_size, &error_abort); + object_property_set_uint(obj, PCI_HOST_BELOW_4G_MMIO_BASE, mmio32_base, + &error_abort); + object_property_set_int(obj, PCI_HOST_BELOW_4G_MMIO_SIZE, mmio32_size, + &error_abort); + object_property_set_uint(obj, PCI_HOST_ABOVE_4G_MMIO_BASE, mmio64_base, + &error_abort); + object_property_set_int(obj, PCI_HOST_ABOVE_4G_MMIO_SIZE, mmio64_size, + &error_abort); + object_property_set_uint(obj, PCI_HOST_PIO_BASE, pio_base, &error_abort); + object_property_set_int(obj, PCI_HOST_PIO_SIZE, pio_size, &error_abort); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + ecam_alias = g_new0(MemoryRegion, 1); + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + snprintf(name, sizeof(name), "pcie.ecam"); + memory_region_init_alias(ecam_alias, obj, name, + ecam_reg, 0, ecam_size); + memory_region_add_subregion(mr, ecam_base, ecam_alias); + + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + + mmio32_alias = g_new0(MemoryRegion, 1); + snprintf(name, sizeof(name), "pcie.mmio32"); + memory_region_init_alias(mmio32_alias, obj, name, + mmio_reg, mmio32_base & 0xffffffffUL, mmio32_size); + memory_region_add_subregion(mr, mmio32_base, mmio32_alias); + + mmio64_alias = g_new0(MemoryRegion, 1); + snprintf(name, sizeof(name), "pcie.mmio64"); + memory_region_init_alias(mmio64_alias, obj, name, + mmio_reg, mmio64_base, mmio64_size); + memory_region_add_subregion(mr, mmio64_base, mmio64_alias); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); + + for (i = 0; i < PCI_NUM_PINS; i++) { + irq = qdev_get_gpio_in(s->irqchip, legacy_irq + i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); + gpex_set_irq_num(GPEX_HOST(dev), i, legacy_irq + i); + } + + gpex_host->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus; +} + +static void gpex_pcie_init(TTAtlantisState *s, MemoryRegion *mr) +{ + gpex_pcie_init_one(s, &s->gpex_host, mr, + &s->memmap[TT_ATL_PCIE_ECAM0], + &s->memmap[TT_ATL_PCIE_PIO0], + &s->memmap[TT_ATL_PCIE_MMIO0_32], + &s->memmap[TT_ATL_PCIE_MMIO0_64], + TT_ATL_PCIE0_INTA_IRQ); +} + static DeviceState *create_reboot_device(const MemMapEntry *mem) { DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); @@ -588,6 +802,9 @@ static void tt_atlantis_machine_init(MachineState *machine) s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus); rom_set_fw(s->fw_cfg); + /* PCIe */ + gpex_pcie_init(s, system_memory); + /* Reboot and exit */ create_reboot_device(&s->memmap[TT_ATL_SYSCON]); @@ -623,6 +840,7 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data) mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; + mc->pci_allow_0_address = true; mc->default_ram_id = "tt_atlantis.ram"; } diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 89e3661726a1..5317eccd06e7 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -129,6 +129,8 @@ config TENSTORRENT select DEVICE_TREE select RISCV_NUMA select PVPANIC_MMIO + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE select SERIAL_MM select RISCV_ACLINT select RISCV_APLIC -- 2.47.3
