Map the PCI memory aperture and PEXP register bank. Apply SUBID
controlled byte swapping to transactions between the big endian CPU and
little endian PCI memory space.
---
hw/mips/octeon.c | 26 ++++++
hw/mips/octeon_internal.h | 8 ++
hw/pci-bridge/octeon_pci.c | 174 +++++++++++++++++++++++++++++++++++++
3 files changed, 208 insertions(+)
diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c
index 19d1451464..596fd678ff 100644
--- a/hw/mips/octeon.c
+++ b/hw/mips/octeon.c
@@ -2075,6 +2075,27 @@ static void octeon_stateful_io_write(OcteonState *s,
uint64_t base,
octeon_csr_store(s, reg, octeon_write64(old, addr, value, size));
}
+static uint64_t octeon_pexp_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return octeon_pci_pexp_read(opaque, addr, size);
+}
+
+static void octeon_pexp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ octeon_pci_pexp_write(opaque, addr, value, size);
+}
+
+static const MemoryRegionOps octeon_pexp_ops = {
+ .read = octeon_pexp_read,
+ .write = octeon_pexp_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
static uint64_t octeon_dpi_read(void *opaque, hwaddr addr, unsigned size)
{
return octeon_stateful_io_read(opaque, OCTEON_DPI_BASE, addr, size);
@@ -2854,6 +2875,11 @@ static void mips_octeon_init(MachineState *machine)
memory_region_add_subregion_overlap(get_system_memory(), OCTEON_CSR_BASE,
&s->csr_bank->csr, -1);
+ memory_region_init_io(&s->pexp, NULL, &octeon_pexp_ops, s,
+ "octeon.pexp", OCTEON_PEXP_SIZE);
+ memory_region_add_subregion(get_system_memory(), OCTEON_PEXP_BASE,
+ &s->pexp);
+
memory_region_init_io(&s->dpi, NULL, &octeon_dpi_ops, s,
"octeon.dpi", OCTEON_DPI_SIZE);
memory_region_add_subregion(get_system_memory(), OCTEON_DPI_BASE,
diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h
index c2a88dac46..f4ecbbac4f 100644
--- a/hw/mips/octeon_internal.h
+++ b/hw/mips/octeon_internal.h
@@ -18,6 +18,8 @@
#define OCTEON_CIU_GPIO_COUNT 32
#define OCTEON_CIU3_CP0_IRQ_COUNT 4
#define OCTEON_CIU3_IDT_COUNT (OCTEON_MAX_CPUS * 4)
+#define OCTEON_PEXP_BASE 0x11f0000000000ULL
+#define OCTEON_PEXP_SIZE 0x40000
#define OCTEON_MIO_BOOT_REG_CFG_COUNT 8
#define OCTEON_MIO_BOOT_LOC_SIZE 0x100
#define OCTEON_MIO_EMM_BUF_SIZE 512
@@ -78,6 +80,7 @@ struct OcteonState {
MemoryRegion boot_flash;
MemoryRegion boot_flash_alias;
MemoryRegion cvmseg;
+ MemoryRegion pexp;
MemoryRegion dpi;
MemoryRegion fpa;
MemoryRegion ipd;
@@ -98,6 +101,8 @@ struct OcteonState {
MemoryRegion pcie_cfg;
MemoryRegion pcie_sli_cfg;
MemoryRegion pcie0_io;
+ MemoryRegion pcie0_mem;
+ AddressSpace pcie0_mem_as;
DeviceState *pci_host;
PCIBus *pci_bus;
@@ -122,5 +127,8 @@ uint64_t octeon_pci_pem_read(OcteonState *s, hwaddr reg,
hwaddr addr, unsigned size);
void octeon_pci_pem_write(OcteonState *s, hwaddr reg, hwaddr addr,
uint64_t value, unsigned size);
+uint64_t octeon_pci_pexp_read(OcteonState *s, hwaddr addr, unsigned size);
+void octeon_pci_pexp_write(OcteonState *s, hwaddr addr,
+ uint64_t value, unsigned size);
#endif
diff --git a/hw/pci-bridge/octeon_pci.c b/hw/pci-bridge/octeon_pci.c
index e2f856e164..02c42c4d09 100644
--- a/hw/pci-bridge/octeon_pci.c
+++ b/hw/pci-bridge/octeon_pci.c
@@ -7,6 +7,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/bswap.h"
#include "qapi/error.h"
#include "hw/core/resettable.h"
#include "hw/core/sysbus.h"
@@ -14,6 +15,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
#include "qemu/module.h"
+#include "system/address-spaces.h"
#include "system/memory.h"
#define TYPE_OCTEON_PCI_HOST "octeon-pci-host"
@@ -29,7 +31,15 @@ OBJECT_DECLARE_TYPE(OcteonPCIHostState, OcteonPCIHostClass,
(OCTEON_PCIE_SLI_CFG_PORTS * OCTEON_PCIE_SLI_CFG_PORT_SIZE)
#define OCTEON_PCIE0_IO_BASE 0x11a0400000000ULL
#define OCTEON_PCIE0_IO_SIZE (1ULL << 32)
+#define OCTEON_PCIE0_MEM_BASE 0x11b0000000000ULL
#define OCTEON_PCIE0_MEM_SIZE (1ULL << 32)
+#define OCTEON_SLI_MEM_ACCESS_SUBID_BASE 0x000000e0
+#define OCTEON_SLI_MEM_ACCESS_SUBID_STRIDE 16
+#define OCTEON_SLI_MEM_ACCESS_SUBID_ESR_M 0x0000003000000000ULL
+#define OCTEON_SLI_MEM_ACCESS_SUBID_ESR_S 36
+#define OCTEON_SLI_MEM_ACCESS_SUBID_ESW_M 0x0000000c00000000ULL
+#define OCTEON_SLI_MEM_ACCESS_SUBID_ESW_S 34
+#define OCTEON_SLI_MEM_ACCESS_SWAP 3
#define OCTEON_PCIE0_ENDPOINT_DEVFN PCI_DEVFN(0x0b, 0)
#define OCTEON_PEMX_CFG_WR0 0xc0000028
#define OCTEON_PEMX_CFG_RD0 0xc0000030
@@ -45,6 +55,7 @@ struct OcteonPCIHostState {
uint32_t pem_cfg_rd_addr;
uint64_t pem_cfg_wr;
GHashTable *root_cfg;
+ GHashTable *pexp_regs;
};
struct OcteonPCIHostClass {
@@ -64,6 +75,7 @@ static void octeon_pci_host_reset_hold(Object *obj, ResetType
type)
host->pem_cfg_rd_addr = 0;
host->pem_cfg_wr = 0;
g_hash_table_remove_all(host->root_cfg);
+ g_hash_table_remove_all(host->pexp_regs);
}
static OcteonPCIHostState *octeon_pci_host(OcteonState *s)
@@ -160,6 +172,34 @@ void octeon_pci_pem_write(OcteonState *s, hwaddr reg,
hwaddr addr,
}
}
+uint64_t octeon_pci_pexp_read(OcteonState *s, hwaddr addr, unsigned size)
+{
+ OcteonPCIHostState *host = octeon_pci_host(s);
+ uint64_t reg = OCTEON_PEXP_BASE + (addr & ~7ULL);
+ uint64_t value;
+
+ if (!octeon_reg_lookup(host->pexp_regs, reg, &value)) {
+ value = 0;
+ }
+
+ return octeon_read64(value, addr, size);
+}
+
+void octeon_pci_pexp_write(OcteonState *s, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ OcteonPCIHostState *host = octeon_pci_host(s);
+ uint64_t reg = OCTEON_PEXP_BASE + (addr & ~7ULL);
+ uint64_t old;
+
+ if (!octeon_reg_lookup(host->pexp_regs, reg, &old)) {
+ old = 0;
+ }
+
+ octeon_reg_store(host->pexp_regs, reg,
+ octeon_write64(old, addr, value, size));
+}
+
static uint64_t octeon_pci_absent(unsigned size)
{
return size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffffU;
@@ -256,6 +296,130 @@ static const MemoryRegionOps octeon_pcie_sli_cfg_ops = {
},
};
+static uint64_t octeon_pcie_bswap(uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ return value;
+ case 2:
+ return bswap16(value);
+ case 4:
+ return bswap32(value);
+ case 8:
+ return bswap64(value);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t octeon_pcie_mem_subid_reg(hwaddr addr)
+{
+ unsigned int subid = (addr >> 28) & 3;
+
+ return OCTEON_PEXP_BASE + OCTEON_SLI_MEM_ACCESS_SUBID_BASE +
+ subid * OCTEON_SLI_MEM_ACCESS_SUBID_STRIDE;
+}
+
+static bool octeon_pcie_mem_swap_enabled(OcteonPCIHostState *host,
+ hwaddr addr, bool write)
+{
+ uint64_t value;
+ uint64_t mask = write ? OCTEON_SLI_MEM_ACCESS_SUBID_ESW_M :
+ OCTEON_SLI_MEM_ACCESS_SUBID_ESR_M;
+ unsigned int shift = write ? OCTEON_SLI_MEM_ACCESS_SUBID_ESW_S :
+ OCTEON_SLI_MEM_ACCESS_SUBID_ESR_S;
+
+ if (!octeon_reg_lookup(host->pexp_regs,
+ octeon_pcie_mem_subid_reg(addr), &value)) {
+ return false;
+ }
+
+ return ((value & mask) >> shift) == OCTEON_SLI_MEM_ACCESS_SWAP;
+}
+
+static uint64_t octeon_pcie0_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ OcteonState *s = opaque;
+ OcteonPCIHostState *host = octeon_pci_host(s);
+ uint64_t value;
+
+ switch (size) {
+ case 1:
+ value = address_space_ldub(&s->pcie0_mem_as, addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 2:
+ value = address_space_lduw_le(&s->pcie0_mem_as, addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 4:
+ value = address_space_ldl_le(&s->pcie0_mem_as, addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 8:
+ value = address_space_ldq_le(&s->pcie0_mem_as, addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (TARGET_BIG_ENDIAN &&
+ !octeon_pcie_mem_swap_enabled(host, addr, false)) {
+ value = octeon_pcie_bswap(value, size);
+ }
+
+ return value;
+}
+
+static void octeon_pcie0_mem_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ OcteonState *s = opaque;
+ OcteonPCIHostState *host = octeon_pci_host(s);
+
+ if (TARGET_BIG_ENDIAN &&
+ !octeon_pcie_mem_swap_enabled(host, addr, true)) {
+ value = octeon_pcie_bswap(value, size);
+ }
+
+ switch (size) {
+ case 1:
+ address_space_stb(&s->pcie0_mem_as, addr, value,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 2:
+ address_space_stw_le(&s->pcie0_mem_as, addr, value,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 4:
+ address_space_stl_le(&s->pcie0_mem_as, addr, value,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 8:
+ address_space_stq_le(&s->pcie0_mem_as, addr, value,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static const MemoryRegionOps octeon_pcie0_mem_ops = {
+ .read = octeon_pcie0_mem_read,
+ .write = octeon_pcie0_mem_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+};
+
static int octeon_pci_map_irq(PCIDevice *pci_dev, int irq_num)
{
return 0;
@@ -273,6 +437,9 @@ static void octeon_pci_host_init(Object *obj)
host->root_cfg = g_hash_table_new_full(octeon_uint64_hash,
octeon_uint64_equal,
g_free, g_free);
+ host->pexp_regs = g_hash_table_new_full(octeon_uint64_hash,
+ octeon_uint64_equal,
+ g_free, g_free);
}
static void octeon_pci_host_finalize(Object *obj)
@@ -280,6 +447,7 @@ static void octeon_pci_host_finalize(Object *obj)
OcteonPCIHostState *host = OCTEON_PCI_HOST(obj);
g_hash_table_destroy(host->root_cfg);
+ g_hash_table_destroy(host->pexp_regs);
}
static void octeon_pci_host_class_init(ObjectClass *klass, const void *data)
@@ -353,4 +521,10 @@ void octeon_init_pci(OcteonState *s)
memory_region_add_subregion(get_system_memory(), OCTEON_PCIE0_IO_BASE,
&s->pcie0_io);
+ address_space_init(&s->pcie0_mem_as, &host->mem,
+ "octeon.pcie0-mem-as");
+ memory_region_init_io(&s->pcie0_mem, NULL, &octeon_pcie0_mem_ops, s,
+ "octeon.pcie0-mem", OCTEON_PCIE0_MEM_SIZE);
+ memory_region_add_subregion(get_system_memory(), OCTEON_PCIE0_MEM_BASE,
+ &s->pcie0_mem);
}
--
2.54.0