diff mbox

pci: Add callbacks to support retreiving and updating interrupts

Message ID 20101029152703.9046.17453.stgit@s20.home (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Williamson Oct. 29, 2010, 3:27 p.m. UTC
None
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.  */