diff mbox

pci: Add callbacks to support retreiving and updating interrupts

Message ID 20101029152703.9046.17453.stgit@s20.home
State New
Headers show

Commit Message

Alex Williamson Oct. 29, 2010, 3:27 p.m. UTC
For device assignment, we need to be able to retreive the IRQ that
the device interrupt pin is assigned to and be notified if that
mapping changes (this happens via ACPI interrupt link updates on
x86).  We can then make use of the IRQ for EOI notification.
Current qemu-kvm device assignment code invades common code with
some hard coded hacks to achieve this.  This attempts to architect
the solution.

Chipset components responsible for interrupt mapping can call
pci_bridge_update_irqs() to signal when interrupt mapping may have
changed.  The bridge for those chipsets should then implement a
get_irq callback.  This is stubbed out for everybody as I only
know how PIIX3 works.  Devices wishing to be notified about IRQ
updates can register via pci_register_update_irqs(), where they
can then check mappings with pci_get_irq().

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---

 hw/apb_pci.c       |    4 ++--
 hw/bonito.c        |    2 +-
 hw/grackle_pci.c   |    2 +-
 hw/gt64xxx.c       |    5 +++--
 hw/pci.c           |   52 +++++++++++++++++++++++++++++++++++++++++++++++-----
 hw/pci.h           |   16 ++++++++++++----
 hw/piix_pci.c      |   23 ++++++++++++++++++++++-
 hw/ppc4xx_pci.c    |    2 +-
 hw/ppce500_pci.c   |    2 +-
 hw/prep_pci.c      |    2 +-
 hw/sh_pci.c        |    2 +-
 hw/unin_pci.c      |   10 ++++++----
 hw/versatile_pci.c |    2 +-
 13 files changed, 99 insertions(+), 25 deletions(-)
diff mbox

Patch

diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 0ecac55..47ff0d9 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -336,8 +336,8 @@  PCIBus *pci_apb_init(target_phys_addr_t special_base,
     d = FROM_SYSBUS(APBState, s);
 
     d->bus = pci_register_bus(&d->busdev.qdev, "pci",
-                                         pci_apb_set_irq, pci_pbm_map_irq, d,
-                                         0, 32);
+                                         pci_apb_set_irq, NULL,
+                                         pci_pbm_map_irq, d, 0, 32);
     pci_bus_set_mem_base(d->bus, mem_base);
 
     for (i = 0; i < 32; i++) {
diff --git a/hw/bonito.c b/hw/bonito.c
index dcf0311..d2869bb 100644
--- a/hw/bonito.c
+++ b/hw/bonito.c
@@ -772,7 +772,7 @@  PCIBus *bonito_init(qemu_irq *pic)
     dev = qdev_create(NULL, "Bonito-pcihost");
     pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev));
     b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq,
-                         pci_bonito_map_irq, pic, 0x28, 32);
+                         NULL, pci_bonito_map_irq, pic, 0x28, 32);
     pcihost->bus = b;
     qdev_init_nofail(dev);
 
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
index 91c755f..e747d7e 100644
--- a/hw/grackle_pci.c
+++ b/hw/grackle_pci.c
@@ -89,7 +89,7 @@  PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic)
     s = sysbus_from_qdev(dev);
     d = FROM_SYSBUS(GrackleState, s);
     d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
-                                         pci_grackle_set_irq,
+                                         pci_grackle_set_irq, NULL,
                                          pci_grackle_map_irq,
                                          pic, 0, 4);
 
diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c
index cabf7ea..2a0fc4a 100644
--- a/hw/gt64xxx.c
+++ b/hw/gt64xxx.c
@@ -1114,8 +1114,9 @@  PCIBus *pci_gt64120_init(qemu_irq *pic)
     s->pci = qemu_mallocz(sizeof(GT64120PCIState));
 
     s->pci->bus = pci_register_bus(NULL, "pci",
-                                   pci_gt64120_set_irq, pci_gt64120_map_irq,
-                                   pic, PCI_DEVFN(18, 0), 4);
+                                   pci_gt64120_set_irq, NULL,
+                                   pci_gt64120_map_irq, pic,
+                                   PCI_DEVFN(18, 0), 4);
     s->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, s);
     d = pci_register_device(s->pci->bus, "GT64120 PCI Bus", sizeof(PCIDevice),
                             0, NULL, NULL);
diff --git a/hw/pci.c b/hw/pci.c
index 1280d4d..645b119 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -41,6 +41,7 @@  struct PCIBus {
     BusState qbus;
     int devfn_min;
     pci_set_irq_fn set_irq;
+    pci_get_irq_fn get_irq;
     pci_map_irq_fn map_irq;
     pci_hotplug_fn hotplug;
     DeviceState *hotplug_qdev;
@@ -139,6 +140,23 @@  static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
     bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
 }
 
+int pci_get_irq(PCIDevice *pci_dev, int pin)
+{
+    PCIBus *bus;
+    for (;;) {
+        if (!pci_dev)
+            return -ENOSYS;
+        bus = pci_dev->bus;
+        if (!bus)
+            return -ENOSYS;
+        pin = bus->map_irq(pci_dev, pin);
+        if (bus->get_irq)
+            break;
+        pci_dev = bus->parent_dev;
+    }
+    return bus->get_irq(bus->irq_opaque, pin);
+}
+
 /* Update interrupt status bit in config space on interrupt
  * state change. */
 static void pci_update_irq_status(PCIDevice *dev)
@@ -260,10 +278,11 @@  PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min)
     return bus;
 }
 
-void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
-                  void *irq_opaque, int nirq)
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+                  pci_map_irq_fn map_irq, void *irq_opaque, int nirq)
 {
     bus->set_irq = set_irq;
+    bus->get_irq = get_irq;
     bus->map_irq = map_irq;
     bus->irq_opaque = irq_opaque;
     bus->nirq = nirq;
@@ -283,13 +302,14 @@  void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
 }
 
 PCIBus *pci_register_bus(DeviceState *parent, const char *name,
-                         pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
-                         void *irq_opaque, int devfn_min, int nirq)
+                         pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+                         pci_map_irq_fn map_irq, void *irq_opaque,
+                         int devfn_min, int nirq)
 {
     PCIBus *bus;
 
     bus = pci_bus_new(parent, name, devfn_min);
-    pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
+    pci_bus_irqs(bus, set_irq, get_irq, map_irq, irq_opaque, nirq);
     return bus;
 }
 
@@ -1541,6 +1561,28 @@  typedef struct {
     uint32_t did;
 } PCIBridge;
 
+void pci_register_update_irqs(PCIDevice *d, PCIUpdateIRQs *update_irqs)
+{
+    d->update_irqs = update_irqs;
+}
+
+static void pci_bridge_update_irqs_fn(PCIBus *b, PCIDevice *d)
+{
+    if (d->update_irqs) {
+        d->update_irqs(d);
+    }
+}
+
+void pci_bridge_update_irqs(PCIBus *b)
+{
+    PCIBus *child;
+
+    pci_for_each_device_under_bus(b, pci_bridge_update_irqs_fn);
+
+    QLIST_FOREACH(child, &b->child, sibling) {
+        pci_bridge_update_irqs(child);
+    }
+}
 
 static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
 {
diff --git a/hw/pci.h b/hw/pci.h
index 3d23f03..1502dc1 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -77,6 +77,7 @@  typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
                                 uint32_t address, uint32_t data, int len);
 typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
                                    uint32_t address, int len);
+typedef void PCIUpdateIRQs(PCIDevice *pci_dev);
 typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
                                 pcibus_t addr, pcibus_t size, int type);
 typedef int PCIUnregisterFunc(PCIDevice *pci_dev);
@@ -141,6 +142,7 @@  struct PCIDevice {
     /* do not access the following fields */
     PCIConfigReadFunc *config_read;
     PCIConfigWriteFunc *config_write;
+    PCIUpdateIRQs *update_irqs;
 
     /* IRQ objects for the INTA-INTD pins.  */
     qemu_irq *irq;
@@ -201,18 +203,24 @@  void pci_default_write_config(PCIDevice *d,
 void pci_device_save(PCIDevice *s, QEMUFile *f);
 int pci_device_load(PCIDevice *s, QEMUFile *f);
 
+int pci_get_irq(PCIDevice *pci_dev, int pin);
+
 typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level);
+typedef int (*pci_get_irq_fn)(void *opaque, int irq_num);
 typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
 typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, int state);
 void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
                          const char *name, int devfn_min);
 PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min);
-void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
-                  void *irq_opaque, int nirq);
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+                  pci_map_irq_fn map_irq, void *irq_opaque, int nirq);
+void pci_bridge_update_irqs(PCIBus *bus);
+void pci_register_update_irqs(PCIDevice *pci_dev, PCIUpdateIRQs *update_irqs);
 void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
 PCIBus *pci_register_bus(DeviceState *parent, const char *name,
-                         pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
-                         void *irq_opaque, int devfn_min, int nirq);
+                         pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+                         pci_map_irq_fn map_irq, void *irq_opaque,
+                         int devfn_min, int nirq);
 
 void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
 
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index b5589b9..8c3f845 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -55,7 +55,10 @@  struct PCII440FXState {
 #define I440FX_PAM_SIZE 7
 #define I440FX_SMRAM    0x72
 
+#define PIIX_CONFIG_IRQ_ROUTE 0x60
+
 static void piix3_set_irq(void *opaque, int irq_num, int level);
+static int piix3_get_irq(void *opaque, int irq_num);
 
 /* return the global irq number corresponding to a given device irq
    pin. We could also use the bus number to have a more precise
@@ -236,7 +239,7 @@  PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *
     piix3 = DO_UPCAST(PIIX3State, dev,
                       pci_create_simple_multifunction(b, -1, true, "PIIX3"));
     piix3->pic = pic;
-    pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, 4);
+    pci_bus_irqs(b, piix3_set_irq, piix3_get_irq, pci_slot_get_pirq, piix3, 4);
     (*pi440fx_state)->piix3 = piix3;
 
     *piix3_devfn = piix3->dev.devfn;
@@ -273,6 +276,13 @@  static void piix3_set_irq(void *opaque, int irq_num, int level)
     }
 }
 
+static int piix3_get_irq(void *opaque, int irq_num)
+{
+    PIIX3State *piix3 = opaque;
+
+    return piix3->dev.config[0x60 + irq_num];
+}
+
 static void piix3_reset(void *opaque)
 {
     PIIX3State *d = opaque;
@@ -311,6 +321,16 @@  static void piix3_reset(void *opaque)
     pci_conf[0xae] = 0x00;
 
     memset(d->pci_irq_levels, 0, sizeof(d->pci_irq_levels));
+    pci_bridge_update_irqs(d->dev.bus);
+}
+
+static void piix3_write_config(PCIDevice *dev,
+                               uint32_t address, uint32_t val, int len)
+{
+    pci_default_write_config(dev, address, val, len);
+    if (ranges_overlap(address, len, PIIX_CONFIG_IRQ_ROUTE, 4)) {
+        pci_bridge_update_irqs(dev->bus);
+    }
 }
 
 static const VMStateDescription vmstate_piix3 = {
@@ -357,6 +377,7 @@  static PCIDeviceInfo i440fx_info[] = {
         .qdev.vmsd    = &vmstate_piix3,
         .qdev.no_user = 1,
         .init         = piix3_initfn,
+        .config_write = piix3_write_config,
     },{
         /* end of list */
     }
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
index 6e437e7..5f3faf7 100644
--- a/hw/ppc4xx_pci.c
+++ b/hw/ppc4xx_pci.c
@@ -358,7 +358,7 @@  PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
     controller = qemu_mallocz(sizeof(PPC4xxPCIState));
 
     controller->pci_state.bus = pci_register_bus(NULL, "pci",
-                                                 ppc4xx_pci_set_irq,
+                                                 ppc4xx_pci_set_irq, NULL,
                                                  ppc4xx_pci_map_irq,
                                                  pci_irqs, 0, 4);
 
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
index 8ac99f2..a6e64d4 100644
--- a/hw/ppce500_pci.c
+++ b/hw/ppce500_pci.c
@@ -277,7 +277,7 @@  PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers)
     controller = qemu_mallocz(sizeof(PPCE500PCIState));
 
     controller->pci_state.bus = pci_register_bus(NULL, "pci",
-                                                 mpc85xx_pci_set_irq,
+                                                 mpc85xx_pci_set_irq, NULL,
                                                  mpc85xx_pci_map_irq,
                                                  pci_irqs, PCI_DEVFN(0x11, 0),
                                                  4);
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
index 0c2afe9..d2e5c4b 100644
--- a/hw/prep_pci.c
+++ b/hw/prep_pci.c
@@ -118,7 +118,7 @@  PCIBus *pci_prep_init(qemu_irq *pic)
 
     s = qemu_mallocz(sizeof(PREPPCIState));
     s->bus = pci_register_bus(NULL, "pci",
-                              prep_set_irq, prep_map_irq, pic, 0, 4);
+                              prep_set_irq, NULL, prep_map_irq, pic, 0, 4);
 
     pci_host_conf_register_ioport(0xcf8, s);
 
diff --git a/hw/sh_pci.c b/hw/sh_pci.c
index cc2f190..b39c78b 100644
--- a/hw/sh_pci.c
+++ b/hw/sh_pci.c
@@ -99,7 +99,7 @@  PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
 
     p = qemu_mallocz(sizeof(SHPCIC));
     p->bus = pci_register_bus(NULL, "pci",
-                              set_irq, map_irq, opaque, devfn_min, nirq);
+                              set_irq, NULL, map_irq, opaque, devfn_min, nirq);
 
     p->dev = pci_register_device(p->bus, "SH PCIC", sizeof(PCIDevice),
                                  -1, NULL, NULL);
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
index 1310211..ec2f246 100644
--- a/hw/unin_pci.c
+++ b/hw/unin_pci.c
@@ -229,8 +229,9 @@  PCIBus *pci_pmac_init(qemu_irq *pic)
     s = sysbus_from_qdev(dev);
     d = FROM_SYSBUS(UNINState, s);
     d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
-                                         pci_unin_set_irq, pci_unin_map_irq,
-                                         pic, PCI_DEVFN(11, 0), 4);
+                                         pci_unin_set_irq, NULL,
+                                         pci_unin_map_irq, pic,
+                                         PCI_DEVFN(11, 0), 4);
 
 #if 0
     pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
@@ -281,8 +282,9 @@  PCIBus *pci_pmac_u3_init(qemu_irq *pic)
     d = FROM_SYSBUS(UNINState, s);
 
     d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
-                                         pci_unin_set_irq, pci_unin_map_irq,
-                                         pic, PCI_DEVFN(11, 0), 4);
+                                         pci_unin_set_irq, NULL,
+                                         pci_unin_map_irq, pic,
+                                         PCI_DEVFN(11, 0), 4);
 
     sysbus_mmio_map(s, 0, 0xf0800000);
     sysbus_mmio_map(s, 1, 0xf0c00000);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index a76bdfa..7f85e77 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -126,7 +126,7 @@  static int pci_vpb_init(SysBusDevice *dev)
         sysbus_init_irq(dev, &s->irq[i]);
     }
     bus = pci_register_bus(&dev->qdev, "pci",
-                           pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
+                           pci_vpb_set_irq, NULL, pci_vpb_map_irq, s->irq,
                            PCI_DEVFN(11, 0), 4);
 
     /* ??? Register memory space.  */