From: deller@kernel.org
To: qemu-devel@nongnu.org
Cc: "Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Richard Henderson" <richard.henderson@linaro.org>,
"BALATON Zoltan" <balaton@eik.bme.hu>,
"Helge Deller" <deller@gmx.de>
Subject: [PATCH v2 03/11] hw/pci-host: Add Astro system bus adapter found on PA-RISC machines
Date: Tue, 17 Oct 2023 17:46:37 +0200 [thread overview]
Message-ID: <20231017154645.95844-4-deller@kernel.org> (raw)
In-Reply-To: <20231017154645.95844-1-deller@kernel.org>
From: Helge Deller <deller@gmx.de>
The 64-bit PA-RISC machines use a Astro system bus adapter (SBA)
with Elroy PCI host chips.
Later generation Astro chips were named Pluto, Ike and REO.
Signed-off-by: Helge Deller <deller@gmx.de>
---
hw/pci-host/astro.c | 876 ++++++++++++++++++++++++++++++++++++
hw/pci-host/trace-events | 11 +
include/hw/pci-host/astro.h | 92 ++++
3 files changed, 979 insertions(+)
create mode 100644 hw/pci-host/astro.c
create mode 100644 include/hw/pci-host/astro.h
diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c
new file mode 100644
index 0000000000..0adbf470c3
--- /dev/null
+++ b/hw/pci-host/astro.c
@@ -0,0 +1,876 @@
+/*
+ * HP-PARISC Astro/Pluto/Ike/REO system bus adapter (SBA)
+ * with Elroy PCI bus (LBA) adapter emulation
+ * Found in C3000 and similar machines
+ *
+ * (C) 2023 by Helge Deller <deller@gmx.de>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ * Chip documentation is available at:
+ * https://parisc.wiki.kernel.org/index.php/Technical_Documentation
+ *
+ * TODO:
+ * - All user-added devices are currently attached to the first
+ * Elroy (PCI bus) only for now. To fix this additional work in
+ * SeaBIOS and this driver is needed. See "user_creatable" flag below.
+ * - GMMIO (Greater than 4 GB MMIO) register
+ */
+
+#define TYPE_ASTRO_IOMMU_MEMORY_REGION "astro-iommu-memory-region"
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "hw/pci/pci_device.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci-host/astro.h"
+#include "hw/hppa/hppa_hardware.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+#include "qom/object.h"
+
+/*
+ * Helper functions
+ */
+
+static uint64_t mask_32bit_val(hwaddr addr, unsigned size, uint64_t val)
+{
+ if (size == 8) {
+ return val;
+ }
+ if (addr & 4) {
+ val >>= 32;
+ } else {
+ val = (uint32_t) val;
+ }
+ return val;
+}
+
+static void put_val_in_int64(uint64_t *p, hwaddr addr, unsigned size,
+ uint64_t val)
+{
+ if (size == 8) {
+ *p = val;
+ } else if (size == 4) {
+ if (addr & 4) {
+ *p = ((*p << 32) >> 32) | (val << 32);
+ } else {
+ *p = ((*p >> 32) << 32) | (uint32_t) val;
+ }
+ }
+}
+
+static void put_val_in_arrary(uint64_t *array, hwaddr start_addr,
+ hwaddr addr, unsigned size, uint64_t val)
+{
+ int index;
+
+ index = (addr - start_addr) / 8;
+ put_val_in_int64(&array[index], addr, size, val);
+}
+
+
+/*
+ * The Elroy PCI host bridge. We have at least 4 of those under Astro.
+ */
+
+static MemTxResult elroy_chip_read_with_attrs(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ MemTxResult ret = MEMTX_OK;
+ ElroyState *s = opaque;
+ uint64_t val = -1;
+ int index;
+
+ switch ((addr >> 3) << 3) {
+ case 0x0008:
+ val = 0x6000005; /* func_class */
+ break;
+ case 0x0058:
+ /*
+ * Scratch register, but firmware initializes it with the
+ * PCI BUS number and Linux/HP-UX uses it then.
+ */
+ val = s->pci_bus_num;
+ /* Upper byte holds the end of this bus number */
+ val |= s->pci_bus_num << 8;
+ break;
+ case 0x0080:
+ val = s->arb_mask; /* set ARB mask */
+ break;
+ case 0x0108:
+ val = s->status_control;
+ break;
+ case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */
+ index = (addr - 0x200) / 8;
+ val = s->mmio_base[index];
+ break;
+ case 0x0680:
+ val = s->error_config;
+ break;
+ case 0x0688:
+ val = 0; /* ERROR_STATUS */
+ break;
+ case 0x0800: /* IOSAPIC_REG_SELECT */
+ val = s->iosapic_reg_select;
+ break;
+ case 0x0808:
+ val = UINT64_MAX; /* XXX: tbc. */
+ g_assert_not_reached();
+ break;
+ case 0x0810: /* IOSAPIC_REG_WINDOW */
+ switch (s->iosapic_reg_select) {
+ case 0x01: /* IOSAPIC_REG_VERSION */
+ val = (32 << 16) | 1; /* upper 16bit holds max entries */
+ break;
+ default:
+ if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) {
+ val = s->iosapic_reg[s->iosapic_reg_select];
+ } else {
+ trace_iosapic_reg_read(s->iosapic_reg_select, size, val);
+ g_assert_not_reached();
+ }
+ }
+ trace_iosapic_reg_read(s->iosapic_reg_select, size, val);
+ break;
+ default:
+ trace_elroy_read(addr, size, val);
+ g_assert_not_reached();
+ }
+ trace_elroy_read(addr, size, val);
+
+ /* for 32-bit accesses mask return value */
+ val = mask_32bit_val(addr, size, val);
+
+ trace_astro_chip_read(addr, size, val);
+ *data = val;
+ return ret;
+}
+
+
+static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ ElroyState *s = opaque;
+ int i;
+
+ trace_elroy_write(addr, size, val);
+
+ switch ((addr >> 3) << 3) {
+ case 0x080:
+ put_val_in_int64(&s->arb_mask, addr, size, val);
+ break;
+ case 0x0108:
+ put_val_in_int64(&s->status_control, addr, size, val);
+ break;
+ case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */
+ put_val_in_arrary(s->mmio_base, 0x200, addr, size, val);
+ break;
+ case 0x0680:
+ put_val_in_int64(&s->error_config, addr, size, val);
+ break;
+ case 0x0800: /* IOSAPIC_REG_SELECT */
+ s->iosapic_reg_select = val;
+ break;
+ case 0x0810: /* IOSAPIC_REG_WINDOW */
+ trace_iosapic_reg_write(s->iosapic_reg_select, size, val);
+ if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) {
+ s->iosapic_reg[s->iosapic_reg_select] = val;
+ } else {
+ g_assert_not_reached();
+ }
+ break;
+ case 0x0840: /* IOSAPIC_REG_EOI */
+ val = le64_to_cpu(val);
+ val &= 63;
+ for (i = 0; i < ELROY_IRQS; i++) {
+ if ((s->iosapic_reg[0x10 + 2 * i] & 63) == val) {
+ s->ilr &= ~(1ull << i);
+ }
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps elroy_chip_ops = {
+ .read_with_attrs = elroy_chip_read_with_attrs,
+ .write_with_attrs = elroy_chip_write_with_attrs,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+
+/* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */
+
+static uint64_t elroy_config_data_read(void *opaque, hwaddr addr, unsigned len)
+{
+ uint64_t val;
+
+ PCIHostState *s = opaque;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
+ trace_elroy_pci_config_data_read(s->config_reg | (addr & 3), len, val);
+ return val;
+}
+
+static void elroy_config_data_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ PCIHostState *s = opaque;
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+ trace_elroy_pci_config_data_write(s->config_reg | (addr & 3), len, val);
+}
+
+static const MemoryRegionOps elroy_config_data_ops = {
+ .read = elroy_config_data_read,
+ .write = elroy_config_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t elroy_config_addr_read(void *opaque, hwaddr addr, unsigned len)
+{
+ ElroyState *s = opaque;
+ return s->config_reg_elroy;
+}
+
+static void elroy_config_addr_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ PCIHostState *s = opaque;
+ ElroyState *es = opaque;
+ es->config_reg_elroy = val; /* keep a copy of original value */
+ s->config_reg = val;
+}
+
+static const MemoryRegionOps elroy_config_addr_ops = {
+ .read = elroy_config_addr_read,
+ .write = elroy_config_addr_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+
+/*
+ * A subroutine of astro_translate_iommu that builds an IOMMUTLBEntry using the
+ * given translated address and mask.
+ */
+static bool make_iommu_tlbe(hwaddr addr, hwaddr taddr, hwaddr mask,
+ IOMMUTLBEntry *ret)
+{
+ hwaddr tce_mask = ~((1ull << 12) - 1);
+ ret->target_as = &address_space_memory;
+ ret->iova = addr & tce_mask;
+ ret->translated_addr = taddr & tce_mask;
+ ret->addr_mask = ~tce_mask;
+ ret->perm = IOMMU_RW;
+ return true;
+}
+
+/* Handle PCI-to-system address translation. */
+static IOMMUTLBEntry astro_translate_iommu(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flag,
+ int iommu_idx)
+{
+ AstroState *s = container_of(iommu, AstroState, iommu);
+ IOMMUTLBEntry ret = {
+ .target_as = &address_space_memory,
+ .iova = addr,
+ .translated_addr = 0,
+ .addr_mask = ~(hwaddr)0,
+ .perm = IOMMU_NONE,
+ };
+ hwaddr pdir_ptr, index, a, ibase;
+ hwaddr addr_mask = 0xfff; /* 4k translation */
+ uint64_t entry;
+
+#define IOVP_SHIFT 12 /* equals PAGE_SHIFT */
+#define PDIR_INDEX(iovp) ((iovp) >> IOVP_SHIFT)
+#define IOVP_MASK PAGE_MASK
+#define SBA_PDIR_VALID_BIT 0x8000000000000000ULL
+
+ /* "range enable" flag cleared? */
+ if ((s->tlb_ibase & 1) == 0) {
+ make_iommu_tlbe(addr, addr, addr_mask, &ret);
+ return ret;
+ }
+
+ a = addr;
+ ibase = s->tlb_ibase & ~1ULL;
+ if ((a & s->tlb_imask) != ibase) {
+ /* do not translate this one! */
+ make_iommu_tlbe(addr, addr, addr_mask, &ret);
+ return ret;
+ }
+ index = PDIR_INDEX(a);
+ pdir_ptr = s->tlb_pdir_base + index * sizeof(entry);
+ entry = ldq_le_phys(&address_space_memory, pdir_ptr);
+ if (!(entry & SBA_PDIR_VALID_BIT)) { /* I/O PDIR entry valid ? */
+ g_assert_not_reached();
+ goto failure;
+ }
+ entry &= ~SBA_PDIR_VALID_BIT;
+ entry >>= IOVP_SHIFT;
+ entry <<= 12;
+ entry |= addr & 0xfff;
+ make_iommu_tlbe(addr, entry, addr_mask, &ret);
+ goto success;
+
+ failure:
+ ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE };
+ success:
+ return ret;
+}
+
+static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque,
+ int devfn)
+{
+ ElroyState *s = opaque;
+ return &s->astro->iommu_as;
+}
+
+/*
+ * Encoding in IOSAPIC:
+ * base_addr == 0xfffa0000, we want to get 0xa0ff0000.
+ * eid 0x0ff00000 -> 0x00ff0000
+ * id 0x000ff000 -> 0xff000000
+ */
+#define SWIZZLE_HPA(a) \
+ ((((a) & 0x0ff00000) >> 4) | (((a) & 0x000ff000) << 12))
+#define UNSWIZZLE_HPA(a) \
+ (((((a) << 4) & 0x0ff00000) | (((a) >> 12) & 0x000ff000) | 0xf0000000))
+
+/* bits in the "low" I/O Sapic IRdT entry */
+#define IOSAPIC_IRDT_DISABLE 0x10000 /* if bit is set, mask this irq */
+#define IOSAPIC_IRDT_PO_LOW 0x02000
+#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000
+#define IOSAPIC_IRDT_MODE_LPRI 0x00100
+
+#define CPU_IRQ_OFFSET 2
+
+static void elroy_set_irq(void *opaque, int irq, int level)
+{
+ ElroyState *s = opaque;
+ uint32_t bit;
+ uint32_t old_ilr = s->ilr;
+ hwaddr cpu_hpa;
+ uint32_t val;
+
+ val = s->iosapic_reg[0x10 + 2 * irq];
+ cpu_hpa = s->iosapic_reg[0x11 + 2 * irq];
+ /* low nibble of val has value to write into CPU irq reg */
+ bit = 1u << (val & (ELROY_IRQS - 1));
+ cpu_hpa = UNSWIZZLE_HPA(cpu_hpa);
+
+ if (level && (!(val & IOSAPIC_IRDT_DISABLE)) && cpu_hpa) {
+ uint32_t ena = bit & ~old_ilr;
+ s->ilr = old_ilr | bit;
+ if (ena != 0) {
+ stl_be_phys(&address_space_memory, cpu_hpa, val & 63);
+ }
+ } else {
+ s->ilr = old_ilr & ~bit;
+ }
+}
+
+static int elroy_pci_map_irq(PCIDevice *d, int irq_num)
+{
+ int slot = PCI_SLOT(d->devfn);
+
+ assert(irq_num >= 0 && irq_num < ELROY_IRQS);
+ return slot & (ELROY_IRQS - 1);
+}
+
+static void elroy_reset(DeviceState *dev)
+{
+ ElroyState *s = ELROY_PCI_HOST_BRIDGE(dev);
+ int irq;
+
+ /*
+ * Make sure to disable interrupts at reboot, otherwise the Linux kernel
+ * serial8250_config_port() in drivers/tty/serial/8250/8250_port.c
+ * will hang during autoconfig().
+ */
+ s->ilr = 0;
+ for (irq = 0; irq < ELROY_IRQS; irq++) {
+ s->iosapic_reg[0x10 + 2 * irq] = IOSAPIC_IRDT_PO_LOW |
+ IOSAPIC_IRDT_LEVEL_TRIG | (irq + CPU_IRQ_OFFSET) |
+ IOSAPIC_IRDT_DISABLE;
+ s->iosapic_reg[0x11 + 2 * irq] = SWIZZLE_HPA(CPU_HPA);
+ }
+}
+
+static void elroy_pcihost_init(Object *obj)
+{
+ ElroyState *s = ELROY_PCI_HOST_BRIDGE(obj);
+ PCIHostState *phb = PCI_HOST_BRIDGE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ /* Elroy config access from CPU. */
+ memory_region_init_io(&s->this_mem, OBJECT(s), &elroy_chip_ops,
+ s, "elroy", 0x2000);
+
+ /* Elroy PCI config. */
+ memory_region_init_io(&phb->conf_mem, OBJECT(phb),
+ &elroy_config_addr_ops, DEVICE(s),
+ "pci-conf-idx", 8);
+ memory_region_init_io(&phb->data_mem, OBJECT(phb),
+ &elroy_config_data_ops, DEVICE(s),
+ "pci-conf-data", 8);
+ memory_region_add_subregion(&s->this_mem, 0x40,
+ &phb->conf_mem);
+ memory_region_add_subregion(&s->this_mem, 0x48,
+ &phb->data_mem);
+
+ /* Elroy PCI bus memory. */
+ memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", UINT64_MAX);
+ memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj,
+ "pci-isa-mmio",
+ ((uint32_t) IOS_DIST_BASE_SIZE) / ROPES_PER_IOC);
+
+ phb->bus = pci_register_root_bus(DEVICE(s), "pci",
+ elroy_set_irq, elroy_pci_map_irq, s,
+ &s->pci_mmio, &s->pci_io,
+ PCI_DEVFN(0, 0), ELROY_IRQS, TYPE_PCI_BUS);
+
+ sysbus_init_mmio(sbd, &s->this_mem);
+
+ qdev_init_gpio_in(DEVICE(obj), elroy_set_irq, ELROY_IRQS);
+}
+
+static Property elroy_pcihost_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_elroy = {
+ .name = "Elroy",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(hpa, ElroyState),
+ VMSTATE_UINT32(pci_bus_num, ElroyState),
+ VMSTATE_UINT64(config_address, ElroyState),
+ VMSTATE_UINT64(config_reg_elroy, ElroyState),
+ VMSTATE_UINT64(status_control, ElroyState),
+ VMSTATE_UINT64(arb_mask, ElroyState),
+ VMSTATE_UINT64_ARRAY(mmio_base, ElroyState, (0x0250 - 0x200) / 8),
+ VMSTATE_UINT64(error_config, ElroyState),
+ VMSTATE_UINT32(iosapic_reg_select, ElroyState),
+ VMSTATE_UINT64_ARRAY(iosapic_reg, ElroyState, 0x20),
+ VMSTATE_UINT32(ilr, ElroyState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void elroy_pcihost_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = elroy_reset;
+ device_class_set_props(dc, elroy_pcihost_properties);
+ dc->vmsd = &vmstate_elroy;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo elroy_pcihost_info = {
+ .name = TYPE_ELROY_PCI_HOST_BRIDGE,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_init = elroy_pcihost_init,
+ .instance_size = sizeof(ElroyState),
+ .class_init = elroy_pcihost_class_init,
+};
+
+static void elroy_register_types(void)
+{
+ type_register_static(&elroy_pcihost_info);
+}
+
+type_init(elroy_register_types)
+
+
+static ElroyState *elroy_init(int num)
+{
+ DeviceState *dev;
+
+ dev = qdev_new(TYPE_ELROY_PCI_HOST_BRIDGE);
+ dev->id = g_strdup_printf("elroy%d", num);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ return ELROY_PCI_HOST_BRIDGE(dev);
+}
+
+/*
+ * Astro Runway chip.
+ */
+
+static MemTxResult astro_chip_read_with_attrs(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ AstroState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+ uint64_t val = -1;
+ int index;
+
+ switch ((addr >> 3) << 3) {
+ /* R2I registers */
+ case 0x0000: /* ID */
+ val = (0x01 << 3) | 0x01ULL;
+ break;
+ case 0x0008: /* IOC_CTRL */
+ val = s->ioc_ctrl;
+ break;
+ case 0x0010: /* TOC_CLIENT_ID */
+ break;
+ case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */
+ val = -1;
+ break;
+ case 0x0300 ... 0x03d8: /* LMMIO_DIRECT0_BASE... */
+ index = (addr - 0x300) / 8;
+ val = s->ioc_ranges[index];
+ break;
+ case 0x10200:
+ val = 0;
+ break;
+ case 0x10220:
+ case 0x10230: /* HP-UX 11.11 reads it. No idea. */
+ val = -1;
+ break;
+ case 0x22108: /* IOC STATUS_CONTROL */
+ val = s->ioc_status_ctrl;
+ break;
+ case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */
+ index = (addr - 0x20200) / 8;
+ val = s->ioc_rope_control[index];
+ break;
+ case 0x20040: /* IOC Rope config */
+ val = s->ioc_rope_config;
+ break;
+ case 0x20050: /* IOC Rope debug */
+ val = 0;
+ break;
+ case 0x20108: /* IOC STATUS_CONTROL */
+ val = s->ioc_status_control;
+ break;
+ case 0x20310: /* IOC_PCOM */
+ val = s->tlb_pcom;
+ /* TODO: flush iommu */
+ break;
+ case 0x20400:
+ val = s->ioc_flush_control;
+ break;
+ /* empty placeholders for non-existent elroys */
+#define EMPTY_PORT(x) case x: case x+8: val = 0; break; \
+ case x+40: case x+48: val = UINT64_MAX; break;
+ EMPTY_PORT(0x30000)
+ EMPTY_PORT(0x32000)
+ EMPTY_PORT(0x34000)
+ EMPTY_PORT(0x36000)
+ EMPTY_PORT(0x38000)
+ EMPTY_PORT(0x3a000)
+ EMPTY_PORT(0x3c000)
+ EMPTY_PORT(0x3e000)
+#undef EMPTY_PORT
+
+ default:
+ trace_astro_chip_read(addr, size, val);
+ g_assert_not_reached();
+ }
+
+ /* for 32-bit accesses mask return value */
+ val = mask_32bit_val(addr, size, val);
+
+ trace_astro_chip_read(addr, size, val);
+ *data = val;
+ return ret;
+}
+
+static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ AstroState *s = opaque;
+
+ trace_astro_chip_write(addr, size, val);
+
+ switch ((addr >> 3) << 3) {
+ case 0x0000: /* ID */
+ break;
+ case 0x0008: /* IOC_CTRL */
+ val &= 0x0ffffff;
+ put_val_in_int64(&s->ioc_ctrl, addr, size, val);
+ break;
+ case 0x0010: /* TOC_CLIENT_ID */
+ break;
+ case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */
+ break;
+ case 0x0300 ... 0x03d8 - 1: /* LMMIO_DIRECT0_BASE... */
+ put_val_in_arrary(s->ioc_ranges, 0x300, addr, size, val);
+ break;
+ case 0x10200:
+ case 0x10220:
+ case 0x10230: /* HP-UX 11.11 reads it. No idea. */
+ break;
+ case 0x22108: /* IOC STATUS_CONTROL */
+ put_val_in_int64(&s->ioc_status_ctrl, addr, size, val);
+ break;
+ case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */
+ put_val_in_arrary(s->ioc_rope_control, 0x20200, addr, size, val);
+ break;
+ case 0x20040: /* IOC Rope config */
+ put_val_in_int64(&s->ioc_rope_config, addr, size, val);
+ break;
+ case 0x20300:
+ put_val_in_int64(&s->tlb_ibase, addr, size, val);
+ break;
+ case 0x20308:
+ put_val_in_int64(&s->tlb_imask, addr, size, val);
+ break;
+ case 0x20310:
+ put_val_in_int64(&s->tlb_pcom, addr, size, val);
+ /* TODO: flush iommu */
+ break;
+ case 0x20318:
+ put_val_in_int64(&s->tlb_tcnfg, addr, size, val);
+ break;
+ case 0x20320:
+ put_val_in_int64(&s->tlb_pdir_base, addr, size, val);
+ break;
+ /*
+ * empty placeholders for non-existent elroys, e.g.
+ * func_class, pci config & data
+ */
+#define EMPTY_PORT(x) case x: case x+8: case x+0x40: case x+0x48:
+ EMPTY_PORT(0x30000)
+ EMPTY_PORT(0x32000)
+ EMPTY_PORT(0x34000)
+ EMPTY_PORT(0x36000)
+ EMPTY_PORT(0x38000)
+ EMPTY_PORT(0x3a000)
+ EMPTY_PORT(0x3c000)
+ EMPTY_PORT(0x3e000)
+ break;
+#undef EMPTY_PORT
+
+ default:
+ /* Controlled by astro_chip_mem_valid above. */
+ trace_astro_chip_write(addr, size, val);
+ g_assert_not_reached();
+ }
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps astro_chip_ops = {
+ .read_with_attrs = astro_chip_read_with_attrs,
+ .write_with_attrs = astro_chip_write_with_attrs,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+static const VMStateDescription vmstate_astro = {
+ .name = "Astro",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(ioc_ctrl, AstroState),
+ VMSTATE_UINT64(ioc_status_ctrl, AstroState),
+ VMSTATE_UINT64_ARRAY(ioc_ranges, AstroState, (0x03d8 - 0x300) / 8),
+ VMSTATE_UINT64(ioc_rope_config, AstroState),
+ VMSTATE_UINT64(ioc_status_control, AstroState),
+ VMSTATE_UINT64(ioc_flush_control, AstroState),
+ VMSTATE_UINT64_ARRAY(ioc_rope_control, AstroState, 8),
+ VMSTATE_UINT64(tlb_ibase, AstroState),
+ VMSTATE_UINT64(tlb_imask, AstroState),
+ VMSTATE_UINT64(tlb_pcom, AstroState),
+ VMSTATE_UINT64(tlb_tcnfg, AstroState),
+ VMSTATE_UINT64(tlb_pdir_base, AstroState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void astro_reset(DeviceState *dev)
+{
+ AstroState *s = ASTRO_CHIP(dev);
+ int i;
+
+ s->ioc_ctrl = 0x29cf;
+ s->ioc_rope_config = 0xc5f;
+ s->ioc_flush_control = 0xb03;
+ s->ioc_status_control = 0;
+ memset(&s->ioc_rope_control, 0, sizeof(s->ioc_rope_control));
+
+ /*
+ * The SBA BASE/MASK registers control CPU -> IO routing.
+ * The LBA BASE/MASK registers control IO -> System routing (in Elroy)
+ */
+ memset(&s->ioc_ranges, 0, sizeof(s->ioc_ranges));
+ s->ioc_ranges[(0x360 - 0x300) / 8] = LMMIO_DIST_BASE_ADDR | 0x01; /* LMMIO_DIST_BASE (SBA) */
+ s->ioc_ranges[(0x368 - 0x300) / 8] = 0xfc000000; /* LMMIO_DIST_MASK */
+ s->ioc_ranges[(0x370 - 0x300) / 8] = 0; /* LMMIO_DIST_ROUTE */
+ s->ioc_ranges[(0x390 - 0x300) / 8] = IOS_DIST_BASE_ADDR | 0x01; /* IOS_DIST_BASE */
+ s->ioc_ranges[(0x398 - 0x300) / 8] = 0xffffff0000; /* IOS_DIST_MASK */
+ s->ioc_ranges[(0x3a0 - 0x300) / 8] = 0x3400000000000000ULL; /* IOS_DIST_ROUTE */
+ s->ioc_ranges[(0x3c0 - 0x300) / 8] = 0xfffee00000; /* IOS_DIRECT_BASE */
+ s->ioc_ranges[(0x3c8 - 0x300) / 8] = 0xffffff0000; /* IOS_DIRECT_MASK */
+ s->ioc_ranges[(0x3d0 - 0x300) / 8] = 0x0; /* IOS_DIRECT_ROUTE */
+
+ s->tlb_ibase = 0;
+ s->tlb_imask = 0;
+ s->tlb_pcom = 0;
+ s->tlb_tcnfg = 0;
+ s->tlb_pdir_base = 0;
+
+ for (i = 0; i < ELROY_NUM; i++) {
+ elroy_reset(DEVICE(s->elroy[i]));
+ }
+}
+
+static void astro_init(Object *obj)
+{
+ AstroState *s = ASTRO_CHIP(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ int i;
+
+ memory_region_init_io(&s->this_mem, OBJECT(s), &astro_chip_ops,
+ s, "astro", 0x40000);
+ sysbus_init_mmio(sbd, &s->this_mem);
+
+ /* Host memory as seen from Elroys PCI side, via the IOMMU. */
+ memory_region_init_iommu(&s->iommu, sizeof(s->iommu),
+ TYPE_ASTRO_IOMMU_MEMORY_REGION, OBJECT(s),
+ "iommu-astro", UINT64_MAX);
+ address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu),
+ "bm-pci");
+
+ /* Create Elroys (PCI host bus chips). */
+ for (i = 0; i < ELROY_NUM; i++) {
+ static const int elroy_hpa_offsets[ELROY_NUM] = {
+ 0x30000, 0x32000, 0x38000, 0x3c000 };
+ static const char elroy_rope_nr[ELROY_NUM] = {
+ 0, 1, 4, 6 }; /* busnum path, e.g. [10:6] */
+ int addr_offset;
+ ElroyState *elroy;
+ hwaddr map_addr;
+ uint64_t map_size;
+ int rope;
+
+ addr_offset = elroy_hpa_offsets[i];
+ rope = elroy_rope_nr[i];
+
+ elroy = elroy_init(i);
+ s->elroy[i] = elroy;
+ elroy->hpa = ASTRO_HPA + addr_offset;
+ elroy->pci_bus_num = i;
+ elroy->astro = s;
+
+ /*
+ * NOTE: we only allow PCI devices on first Elroy for now.
+ * SeaBIOS will not find devices on the other busses.
+ */
+ if (i > 0) {
+ qbus_mark_full(&PCI_HOST_BRIDGE(elroy)->bus->qbus);
+ }
+
+ /* map elroy config addresses into Astro space */
+ memory_region_add_subregion(&s->this_mem, addr_offset,
+ &elroy->this_mem);
+
+ /* LMMIO */
+ elroy->mmio_base[(0x0200 - 0x200) / 8] = 0xf0000001;
+ elroy->mmio_base[(0x0208 - 0x200) / 8] = 0xf8000000;
+ /* GMMIO */
+ elroy->mmio_base[(0x0210 - 0x200) / 8] = 0x000000f800000001;
+ elroy->mmio_base[(0x0218 - 0x200) / 8] = 0x000000ff80000000;
+ /* WLMMIO */
+ elroy->mmio_base[(0x0220 - 0x200) / 8] = 0xf0000001;
+ elroy->mmio_base[(0x0228 - 0x200) / 8] = 0xf0000000;
+ /* WGMMIO */
+ elroy->mmio_base[(0x0230 - 0x200) / 8] = 0x000000f800000001;
+ elroy->mmio_base[(0x0238 - 0x200) / 8] = 0x000000fc00000000;
+ /* IOS_BASE */
+ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC;
+ elroy->mmio_base[(0x0240 - 0x200) / 8] = rope * map_size | 0x01;
+ elroy->mmio_base[(0x0248 - 0x200) / 8] = 0x0000e000;
+
+ /* map elroys mmio */
+ map_size = LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC;
+ map_addr = (uint32_t) (LMMIO_DIST_BASE_ADDR + rope * map_size);
+ memory_region_init_alias(&elroy->pci_mmio_alias, OBJECT(elroy),
+ "pci-mmio-alias",
+ &elroy->pci_mmio, map_addr, map_size);
+ memory_region_add_subregion(get_system_memory(), map_addr,
+ &elroy->pci_mmio_alias);
+
+ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC;
+ map_addr = (uint32_t) (IOS_DIST_BASE_ADDR + rope * map_size);
+ memory_region_add_subregion(get_system_memory(), map_addr,
+ &elroy->pci_io);
+
+ /* Host memory as seen from the PCI side, via the IOMMU. */
+ pci_setup_iommu(PCI_HOST_BRIDGE(elroy)->bus, elroy_pcihost_set_iommu,
+ elroy);
+ }
+}
+
+static void astro_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = astro_reset;
+ dc->vmsd = &vmstate_astro;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo astro_chip_info = {
+ .name = TYPE_ASTRO_CHIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = astro_init,
+ .instance_size = sizeof(AstroState),
+ .class_init = astro_class_init,
+};
+
+static void astro_iommu_memory_region_class_init(ObjectClass *klass,
+ void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = astro_translate_iommu;
+}
+
+static const TypeInfo astro_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_ASTRO_IOMMU_MEMORY_REGION,
+ .class_init = astro_iommu_memory_region_class_init,
+};
+
+
+static void astro_register_types(void)
+{
+ type_register_static(&astro_chip_info);
+ type_register_static(&astro_iommu_memory_region_info);
+}
+
+type_init(astro_register_types)
diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
index 9d216bb89f..b2f47e6335 100644
--- a/hw/pci-host/trace-events
+++ b/hw/pci-host/trace-events
@@ -46,3 +46,14 @@ pnv_phb4_xive_notify_abt(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64"
dino_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d"
dino_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x"
dino_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x"
+
+# astro.c
+astro_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d"
+astro_chip_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+astro_chip_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_pci_config_data_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_pci_config_data_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+iosapic_reg_write(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64
+iosapic_reg_read(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64
diff --git a/include/hw/pci-host/astro.h b/include/hw/pci-host/astro.h
new file mode 100644
index 0000000000..f63fd220f3
--- /dev/null
+++ b/include/hw/pci-host/astro.h
@@ -0,0 +1,92 @@
+/*
+ * HP-PARISC Astro Bus connector with Elroy PCI host bridges
+ */
+
+#ifndef ASTRO_H
+#define ASTRO_H
+
+#include "hw/pci/pci_host.h"
+
+#define ASTRO_HPA 0xfed00000
+
+#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */
+
+#define TYPE_ASTRO_CHIP "astro-chip"
+OBJECT_DECLARE_SIMPLE_TYPE(AstroState, ASTRO_CHIP)
+
+#define TYPE_ELROY_PCI_HOST_BRIDGE "elroy-pcihost"
+OBJECT_DECLARE_SIMPLE_TYPE(ElroyState, ELROY_PCI_HOST_BRIDGE)
+
+#define ELROY_NUM 4 /* # of Elroys */
+#define ELROY_IRQS 8 /* IOSAPIC IRQs */
+
+/* ASTRO Memory and I/O regions */
+#define LMMIO_DIST_BASE_ADDR 0xf4000000ULL
+#define LMMIO_DIST_BASE_SIZE 0x4000000ULL
+
+#define IOS_DIST_BASE_ADDR 0xfffee00000ULL
+#define IOS_DIST_BASE_SIZE 0x10000ULL
+
+struct AstroState;
+
+struct ElroyState {
+ PCIHostState parent_obj;
+
+ /* parent Astro device */
+ struct AstroState *astro;
+
+ /* HPA of this Elroy */
+ hwaddr hpa;
+
+ /* PCI bus number (Elroy number) */
+ unsigned int pci_bus_num;
+
+ uint64_t config_address;
+ uint64_t config_reg_elroy;
+
+ uint64_t status_control;
+ uint64_t arb_mask;
+ uint64_t mmio_base[(0x0250 - 0x200) / 8];
+ uint64_t error_config;
+
+ uint32_t iosapic_reg_select;
+ uint64_t iosapic_reg[0x20];
+
+ uint32_t ilr;
+
+ MemoryRegion this_mem;
+
+ MemoryRegion pci_mmio;
+ MemoryRegion pci_mmio_alias;
+ MemoryRegion pci_hole;
+ MemoryRegion pci_io;
+};
+
+struct AstroState {
+ PCIHostState parent_obj;
+
+ uint64_t ioc_ctrl;
+ uint64_t ioc_status_ctrl;
+ uint64_t ioc_ranges[(0x03d8 - 0x300) / 8];
+ uint64_t ioc_rope_config;
+ uint64_t ioc_status_control;
+ uint64_t ioc_flush_control;
+ uint64_t ioc_rope_control[8];
+ uint64_t tlb_ibase;
+ uint64_t tlb_imask;
+ uint64_t tlb_pcom;
+ uint64_t tlb_tcnfg;
+ uint64_t tlb_pdir_base;
+
+ struct ElroyState *elroy[ELROY_NUM];
+
+ MemoryRegion this_mem;
+
+ MemoryRegion pci_mmio;
+ MemoryRegion pci_io;
+
+ IOMMUMemoryRegion iommu;
+ AddressSpace iommu_as;
+};
+
+#endif
--
2.41.0
next prev parent reply other threads:[~2023-10-17 15:47 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-17 15:46 [PATCH v2 00/11] target/hppa: Add emulation of a C3700 HP-PARISC workstation deller
2023-10-17 15:46 ` [PATCH v2 01/11] target/hppa: Update to SeaBIOS-hppa version 10 deller
2023-10-17 15:46 ` [PATCH v2 02/11] pci_ids/tulip: Add PCI vendor ID for HP and use it in tulip deller
2023-10-17 16:13 ` BALATON Zoltan
2023-10-17 17:58 ` Helge Deller
2023-10-17 19:19 ` BALATON Zoltan
2023-10-17 19:25 ` Helge Deller
2023-10-17 20:21 ` BALATON Zoltan
2023-10-17 20:48 ` Helge Deller
2023-10-17 15:46 ` deller [this message]
2023-10-17 15:46 ` [PATCH v2 04/11] MAINTAINERS: Add Astro PCI host for hppa machines deller
2023-10-17 16:19 ` BALATON Zoltan
2023-10-17 18:05 ` Helge Deller
2023-10-17 19:10 ` BALATON Zoltan
2023-10-17 15:46 ` [PATCH v2 05/11] lasips2: LASI PS/2 devices are not user-createable deller
2023-10-17 16:23 ` BALATON Zoltan
2023-10-17 18:05 ` Helge Deller
2023-10-17 15:46 ` [PATCH v2 06/11] pci-host: Wire up new Astro/Elroy PCI bridge deller
2023-10-17 16:29 ` BALATON Zoltan
2023-10-17 18:08 ` Helge Deller
2023-10-17 15:46 ` [PATCH v2 07/11] hw/hppa: Require at least SeaBIOS-hppa version 10 deller
2023-10-17 15:46 ` [PATCH v2 08/11] hw/hppa: Export machine name, BTLBs, power-button address via fw_cfg deller
2023-10-17 16:33 ` BALATON Zoltan
2023-10-17 18:11 ` Helge Deller
2023-10-17 15:46 ` [PATCH v2 09/11] hw/hppa: Provide RTC and DebugOutputPort on CPU #0 deller
2023-10-17 15:46 ` [PATCH v2 10/11] hw/hppa: Split out machine creation deller
2023-10-17 15:46 ` [PATCH v2 11/11] hw/hppa: Add new HP C3700 machine deller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231017154645.95844-4-deller@kernel.org \
--to=deller@kernel.org \
--cc=balaton@eik.bme.hu \
--cc=deller@gmx.de \
--cc=philmd@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.