diff mbox

[10/10] versatile_pci: Expose PCI memory space to system

Message ID 1364124760-5794-11-git-send-email-peter.maydell@linaro.org
State Superseded
Headers show

Commit Message

Peter Maydell March 24, 2013, 11:32 a.m. UTC
The VersatilePB's PCI controller exposes the PCI memory space to the
system via three regions controlled by the mapping control registers.
Implement this so that guests can actually use MMIO-BAR PCI cards.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/arm/realview.c    |    3 +++
 hw/arm/versatilepb.c |    3 +++
 hw/versatile_pci.c   |   72 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 77 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 23c968b..db41525 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -221,6 +221,9 @@  static void realview_init(QEMUMachineInitArgs *args,
         sysbus_mmio_map(busdev, 1, 0x60000000); /* PCI self-config */
         sysbus_mmio_map(busdev, 2, 0x61000000); /* PCI config */
         sysbus_mmio_map(busdev, 3, 0x62000000); /* PCI I/O */
+        sysbus_mmio_map(busdev, 4, 0x63000000); /* PCI memory window 1 */
+        sysbus_mmio_map(busdev, 5, 0x64000000); /* PCI memory window 2 */
+        sysbus_mmio_map(busdev, 6, 0x68000000); /* PCI memory window 3 */
         sysbus_connect_irq(busdev, 0, pic[48]);
         sysbus_connect_irq(busdev, 1, pic[49]);
         sysbus_connect_irq(busdev, 2, pic[50]);
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 9c9bfde..413f0e9 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -228,6 +228,9 @@  static void versatile_init(QEMUMachineInitArgs *args, int board_id)
     sysbus_mmio_map(busdev, 1, 0x41000000); /* PCI self-config */
     sysbus_mmio_map(busdev, 2, 0x42000000); /* PCI config */
     sysbus_mmio_map(busdev, 3, 0x43000000); /* PCI I/O */
+    sysbus_mmio_map(busdev, 4, 0x44000000); /* PCI memory window 1 */
+    sysbus_mmio_map(busdev, 5, 0x50000000); /* PCI memory window 2 */
+    sysbus_mmio_map(busdev, 6, 0x60000000); /* PCI memory window 3 */
     sysbus_connect_irq(busdev, 0, sic[27]);
     sysbus_connect_irq(busdev, 1, sic[28]);
     sysbus_connect_irq(busdev, 2, sic[29]);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index a617675..05e1ec8 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -20,14 +20,21 @@  typedef struct {
     MemoryRegion controlregs;
     MemoryRegion mem_config;
     MemoryRegion mem_config2;
+    /* Containers representing the PCI address spaces */
     MemoryRegion pci_io_space;
+    MemoryRegion pci_mem_space;
+    /* Alias regions into PCI address spaces which we expose as sysbus regions.
+     * The offsets into pci_mem_space are controlled by the imap registers.
+     */
     MemoryRegion pci_io_window;
+    MemoryRegion pci_mem_window[3];
     PCIBus pci_bus;
     PCIDevice pci_dev;
 
     /* Constant for life of device: */
     int realview;
     uint8_t broken_irq_mapping;
+    uint32_t mem_win_size[3];
 
     /* Register state: */
     uint32_t imap[3];
@@ -36,10 +43,49 @@  typedef struct {
     uint32_t flags;
 } PCIVPBState;
 
+static void pci_vpb_update_window(PCIVPBState *s, int i)
+{
+    /* Adjust the offset of the alias region we use for
+     * the memory window i to account for a change in the
+     * value of the corresponding IMAP register.
+     * Note that the semantics of the IMAP register differ
+     * for realview and versatile variants of the controller.
+     */
+    hwaddr offset;
+    if (s->realview) {
+        /* Top bits of register (masked according to window size) provide
+         * top bits of PCI address.
+         */
+        offset = s->imap[i] & ~(s->mem_win_size[i] - 1);
+    } else {
+        /* Bottom 4 bits of register provide top 4 bits of PCI address */
+        offset = s->imap[i] << 28;
+    }
+    memory_region_set_alias_offset(&s->pci_mem_window[i], offset);
+}
+
+static void pci_vpb_update_all_windows(PCIVPBState *s)
+{
+    /* Update all alias windows based on the current register state */
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        pci_vpb_update_window(s, i);
+    }
+}
+
+static int pci_vpb_post_load(void *opaque, int version_id)
+{
+    PCIVPBState *s = opaque;
+    pci_vpb_update_all_windows(s);
+    return 0;
+}
+
 static const VMStateDescription pci_vpb_vmstate = {
     .name = "versatile-pci",
     .version_id = 1,
     .minimum_version_id = 1,
+    .post_load = pci_vpb_post_load,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32_ARRAY(imap, PCIVPBState, 3),
         VMSTATE_UINT32_ARRAY(smap, PCIVPBState, 3),
@@ -80,6 +126,7 @@  static void pci_vpb_reg_write(void *opaque, hwaddr addr,
     {
         int win = (addr - PCI_IMAP0) >> 2;
         s->imap[win] = val;
+        pci_vpb_update_window(s, win);
         break;
     }
     case PCI_SELFID:
@@ -234,6 +281,8 @@  static void pci_vpb_reset(DeviceState *d)
     s->smap[2] = 0;
     s->selfid = 0;
     s->flags = 0;
+
+    pci_vpb_update_all_windows(s);
 }
 
 static void pci_vpb_init(Object *obj)
@@ -242,9 +291,10 @@  static void pci_vpb_init(Object *obj)
     PCIVPBState *s = PCI_VPB(obj);
 
     memory_region_init(&s->pci_io_space, "pci_io", 1ULL << 32);
+    memory_region_init(&s->pci_mem_space, "pci_mem", 1ULL << 32);
 
     pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), "pci",
-                        get_system_memory(), &s->pci_io_space,
+                        &s->pci_mem_space, &s->pci_io_space,
                         PCI_DEVFN(11, 0));
     h->bus = &s->pci_bus;
 
@@ -252,6 +302,11 @@  static void pci_vpb_init(Object *obj)
     qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus));
     object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(29, 0), "addr",
                             NULL);
+
+    /* Window sizes for VersatilePB; realview_pci's init will override */
+    s->mem_win_size[0] = 0x0c000000;
+    s->mem_win_size[1] = 0x10000000;
+    s->mem_win_size[2] = 0x10000000;
 }
 
 static void pci_vpb_realize(DeviceState *dev, Error **errp)
@@ -280,6 +335,7 @@  static void pci_vpb_realize(DeviceState *dev, Error **errp)
      * 1 : PCI self config window
      * 2 : PCI config window
      * 3 : PCI IO window
+     * 4..6 : PCI memory windows
      */
     memory_region_init_io(&s->controlregs, &pci_vpb_reg_ops, s, "pci-vpb-regs",
                           0x1000);
@@ -299,6 +355,16 @@  static void pci_vpb_realize(DeviceState *dev, Error **errp)
 
     sysbus_init_mmio(sbd, &s->pci_io_space);
 
+    /* Create the alias regions corresponding to our three windows onto
+     * PCI memory space. The sizes vary from board to board; the base
+     * offsets are guest controllable via the IMAP registers.
+     */
+    for (i = 0; i < 3; i++) {
+        memory_region_init_alias(&s->pci_mem_window[i], "pci-vbp-window",
+                                 &s->pci_mem_space, 0, s->mem_win_size[i]);
+        sysbus_init_mmio(sbd, &s->pci_mem_window[i]);
+    }
+
     /* TODO Remove once realize propagates to child devices. */
     object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp);
 }
@@ -356,6 +422,10 @@  static void pci_realview_init(Object *obj)
     PCIVPBState *s = PCI_VPB(obj);
 
     s->realview = 1;
+    /* The PCI window sizes are different on Realview boards */
+    s->mem_win_size[0] = 0x01000000;
+    s->mem_win_size[1] = 0x04000000;
+    s->mem_win_size[2] = 0x08000000;
 }
 
 static const TypeInfo pci_realview_info = {