@@ -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++) {
@@ -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);
@@ -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);
@@ -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);
@@ -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)
{
@@ -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);
@@ -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 */
}
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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. */