aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPhilippe Mathieu-Daudé <philmd@linaro.org>2023-01-04 10:03:14 +0100
committerPhilippe Mathieu-Daudé <philmd@linaro.org>2023-01-13 09:31:19 +0100
commit145e2198d749ec09a405f1607a9932499b76f1eb (patch)
treeab5a7c15c802b7bc1b8a7fee7f080235e532cb1e /hw
parent65423e6efeac1ee1057870361337c572c941140c (diff)
hw/mips/gt64xxx_pci: Endian-swap using PCI_HOST_BRIDGE MemoryRegionOps
GT64120's PCI endianness swapping works on little-endian hosts, but doesn't on big-endian ones. Instead of complicating how CFGADDR/CFGDATA registers deal with endianness, use the existing MemoryRegionOps from hw/pci/pci_host.c. Doing so also reduce the access to internal PCI_HOST_BRIDGE fields. Map the PCI_HOST_BRIDGE MemoryRegionOps into the corresponding CFGADDR/CFGDATA regions in the ISD MMIO and remove the unused code in the current ISD read/write handlers. Update the mapping when PCI0_CMD register is accessed (in case the endianness is changed). This allows using the GT64120 on a big-endian host (and boot the MIPS Malta machine in little-endian). Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20230104133935.4639-6-philmd@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/mips/gt64xxx_pci.c70
1 files changed, 50 insertions, 20 deletions
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
index 65416c7b27..81232514c5 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -298,6 +298,50 @@ static void gt64120_isd_mapping(GT64120State *s)
memory_region_transaction_commit();
}
+static void gt64120_update_pci_cfgdata_mapping(GT64120State *s)
+{
+ /* Indexed on MByteSwap bit, see Table 158: PCI_0 Command, Offset: 0xc00 */
+ static const MemoryRegionOps *pci_host_conf_ops[] = {
+ &pci_host_conf_be_ops, &pci_host_conf_le_ops
+ };
+ static const MemoryRegionOps *pci_host_data_ops[] = {
+ &pci_host_data_be_ops, &pci_host_data_le_ops
+ };
+ PCIHostState *phb = PCI_HOST_BRIDGE(s);
+
+ memory_region_transaction_begin();
+
+ /*
+ * The setting of the MByteSwap bit and MWordSwap bit in the PCI Internal
+ * Command Register determines how data transactions from the CPU to/from
+ * PCI are handled along with the setting of the Endianess bit in the CPU
+ * Configuration Register. See:
+ * - Table 16: 32-bit PCI Transaction Endianess
+ * - Table 158: PCI_0 Command, Offset: 0xc00
+ */
+ if (memory_region_is_mapped(&phb->conf_mem)) {
+ memory_region_del_subregion(&s->ISD_mem, &phb->conf_mem);
+ object_unparent(OBJECT(&phb->conf_mem));
+ }
+ memory_region_init_io(&phb->conf_mem, OBJECT(phb),
+ pci_host_conf_ops[s->regs[GT_PCI0_CMD] & 1],
+ s, "pci-conf-idx", 4);
+ memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGADDR << 2,
+ &phb->conf_mem, 1);
+
+ if (memory_region_is_mapped(&phb->data_mem)) {
+ memory_region_del_subregion(&s->ISD_mem, &phb->data_mem);
+ object_unparent(OBJECT(&phb->data_mem));
+ }
+ memory_region_init_io(&phb->data_mem, OBJECT(phb),
+ pci_host_data_ops[s->regs[GT_PCI0_CMD] & 1],
+ s, "pci-conf-data", 4);
+ memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGDATA << 2,
+ &phb->data_mem, 1);
+
+ memory_region_transaction_commit();
+}
+
static void gt64120_pci_mapping(GT64120State *s)
{
memory_region_transaction_begin();
@@ -389,7 +433,6 @@ static void gt64120_writel(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
GT64120State *s = opaque;
- PCIHostState *phb = PCI_HOST_BRIDGE(s);
uint32_t saddr = addr >> 2;
trace_gt64120_write(addr, val);
@@ -592,6 +635,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
case GT_PCI0_CMD:
case GT_PCI1_CMD:
s->regs[saddr] = val & 0x0401fc0f;
+ gt64120_update_pci_cfgdata_mapping(s);
break;
case GT_PCI0_TOR:
case GT_PCI0_BS_SCS10:
@@ -632,15 +676,9 @@ static void gt64120_writel(void *opaque, hwaddr addr,
saddr << 2, size, size << 1, val);
break;
case GT_PCI0_CFGADDR:
- phb->config_reg = val & 0x80fffffc;
- break;
case GT_PCI0_CFGDATA:
- if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) {
- val = bswap32(val);
- }
- if (phb->config_reg & (1u << 31)) {
- pci_data_write(phb->bus, phb->config_reg, val, 4);
- }
+ /* Mapped via in gt64120_pci_mapping() */
+ g_assert_not_reached();
break;
/* Interrupts */
@@ -698,7 +736,6 @@ static uint64_t gt64120_readl(void *opaque,
hwaddr addr, unsigned size)
{
GT64120State *s = opaque;
- PCIHostState *phb = PCI_HOST_BRIDGE(s);
uint32_t val;
uint32_t saddr = addr >> 2;
@@ -883,17 +920,9 @@ static uint64_t gt64120_readl(void *opaque,
/* PCI Internal */
case GT_PCI0_CFGADDR:
- val = phb->config_reg;
- break;
case GT_PCI0_CFGDATA:
- if (!(phb->config_reg & (1 << 31))) {
- val = 0xffffffff;
- } else {
- val = pci_data_read(phb->bus, phb->config_reg, 4);
- }
- if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) {
- val = bswap32(val);
- }
+ /* Mapped via in gt64120_pci_mapping() */
+ g_assert_not_reached();
break;
case GT_PCI0_CMD:
@@ -1153,6 +1182,7 @@ static void gt64120_reset(DeviceState *dev)
gt64120_isd_mapping(s);
gt64120_pci_mapping(s);
+ gt64120_update_pci_cfgdata_mapping(s);
}
static void gt64120_realize(DeviceState *dev, Error **errp)