PPC: Uninorth config space accessor
authorAlexander Graf <agraf@suse.de>
Tue, 9 Feb 2010 16:37:01 +0000 (17:37 +0100)
committerMichael S. Tsirkin <mst@redhat.com>
Sun, 14 Feb 2010 14:10:54 +0000 (16:10 +0200)
The Uninorth PCI bridge requires different layouts in its PCI config space
accessors.

This patch introduces a conversion function that makes it compatible with
the way Linux accesses it.

I also kept an OpenBIOS compatibility hack in. I think it'd be better to
take small steps here and do the config space access rework in OpenBIOS
later on. When that's done we can remove that hack.

Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
hw/unin_pci.c

index 19eb5e0..b9ff831 100644 (file)
@@ -39,6 +39,7 @@
 typedef struct UNINState {
     SysBusDevice busdev;
     PCIHostState host_state;
+    ReadWriteHandler data_handler;
 } UNINState;
 
 /* Don't know if this matches real hardware, but it agrees with OHW.  */
@@ -75,6 +76,68 @@ static void pci_unin_reset(void *opaque)
 {
 }
 
+static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
+{
+    uint32_t retval;
+
+    if (reg & (1u << 31)) {
+        /* XXX OpenBIOS compatibility hack */
+        retval = reg | (addr & 3);
+    } else if (reg & 1) {
+        /* CFA1 style */
+        retval = (reg & ~7u) | (addr & 7);
+    } else {
+        uint32_t slot, func;
+
+        /* Grab CFA0 style values */
+        slot = ffs(reg & 0xfffff800) - 1;
+        func = (reg >> 8) & 7;
+
+        /* ... and then convert them to x86 format */
+        /* config pointer */
+        retval = (reg & (0xff - 7)) | (addr & 7);
+        /* slot */
+        retval |= slot << 11;
+        /* fn */
+        retval |= func << 8;
+    }
+
+
+    UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
+                 reg, addr, retval);
+
+    return retval;
+}
+
+static void unin_data_write(ReadWriteHandler *handler,
+                            pcibus_t addr, uint32_t val, int len)
+{
+    UNINState *s = container_of(handler, UNINState, data_handler);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    UNIN_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+    pci_data_write(s->host_state.bus,
+                   unin_get_config_reg(s->host_state.config_reg, addr),
+                   val, len);
+}
+
+static uint32_t unin_data_read(ReadWriteHandler *handler,
+                               pcibus_t addr, int len)
+{
+    UNINState *s = container_of(handler, UNINState, data_handler);
+    uint32_t val;
+
+    val = pci_data_read(s->host_state.bus,
+                        unin_get_config_reg(s->host_state.config_reg, addr),
+                        len);
+    UNIN_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    return val;
+}
+
 static int pci_unin_main_init_device(SysBusDevice *dev)
 {
     UNINState *s;
@@ -85,7 +148,9 @@ static int pci_unin_main_init_device(SysBusDevice *dev)
     s = FROM_SYSBUS(UNINState, dev);
 
     pci_mem_config = pci_host_conf_register_mmio(&s->host_state);
-    pci_mem_data = pci_host_data_register_mmio(&s->host_state);
+    s->data_handler.read = unin_data_read;
+    s->data_handler.write = unin_data_write;
+    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler);
     sysbus_init_mmio(dev, 0x1000, pci_mem_config);
     sysbus_init_mmio(dev, 0x1000, pci_mem_data);