diff options
Diffstat (limited to 'hw/isa/vt82c686.c')
-rw-r--r-- | hw/isa/vt82c686.c | 79 |
1 files changed, 52 insertions, 27 deletions
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 57bdfb4e78..9c2333a277 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -549,6 +549,7 @@ struct ViaISAState { PCIDevice dev; qemu_irq cpu_intr; qemu_irq *isa_irqs_in; + uint16_t irq_state[ISA_NUM_IRQS]; ViaSuperIOState via_sio; MC146818RtcState rtc; PCIIDEState ide; @@ -592,15 +593,9 @@ static const TypeInfo via_isa_info = { }, }; -static void via_isa_request_i8259_irq(void *opaque, int irq, int level) -{ - ViaISAState *s = opaque; - qemu_set_irq(s->cpu_intr, level); -} - -static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num) +static int via_isa_get_pci_irq(const ViaISAState *s, int pin) { - switch (irq_num) { + switch (pin) { case 0: return s->dev.config[0x55] >> 4; case 1: @@ -613,29 +608,60 @@ static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num) return 0; } -static void via_isa_set_pci_irq(void *opaque, int irq_num, int level) +void via_isa_set_irq(PCIDevice *d, int pin, int level) { - ViaISAState *s = opaque; - PCIBus *bus = pci_get_bus(&s->dev); - int i, pic_level, pic_irq = via_isa_get_pci_irq(s, irq_num); + ViaISAState *s = VIA_ISA(pci_get_function_0(d)); + uint8_t irq = d->config[PCI_INTERRUPT_LINE], max_irq = 15; + int f = PCI_FUNC(d->devfn); + uint16_t mask = BIT(f); + + switch (f) { + case 0: /* PIRQ/PINT inputs */ + irq = via_isa_get_pci_irq(s, pin); + f = 8 + pin; /* Use function 8-11 for PCI interrupt inputs */ + break; + case 2: /* USB ports 0-1 */ + case 3: /* USB ports 2-3 */ + case 5: /* AC97 audio */ + max_irq = 14; + break; + } - /* IRQ 0: disabled, IRQ 2,8,13: reserved */ - if (!pic_irq) { + /* Keep track of the state of all sources */ + if (level) { + s->irq_state[0] |= mask; + } else { + s->irq_state[0] &= ~mask; + } + if (irq == 0 || irq == 0xff) { + return; /* disabled */ + } + if (unlikely(irq > max_irq || irq == 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing %d for %d", + irq, f); return; } - if (unlikely(pic_irq == 2 || pic_irq == 8 || pic_irq == 13)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing"); + /* Record source state at mapped IRQ */ + if (level) { + s->irq_state[irq] |= mask; + } else { + s->irq_state[irq] &= ~mask; } + /* Make sure there are no stuck bits if mapping has changed */ + s->irq_state[irq] &= s->irq_state[0]; + /* ISA IRQ level is the OR of all sources routed to it */ + qemu_set_irq(s->isa_irqs_in[irq], !!s->irq_state[irq]); +} - /* The pic level is the logical OR of all the PCI irqs mapped to it. */ - pic_level = 0; - for (i = 0; i < PCI_NUM_PINS; i++) { - if (pic_irq == via_isa_get_pci_irq(s, i)) { - pic_level |= pci_bus_get_irq_level(bus, i); - } - } - /* Now we change the pic irq level according to the via irq mappings. */ - qemu_set_irq(s->isa_irqs_in[pic_irq], pic_level); +static void via_isa_pirq(void *opaque, int pin, int level) +{ + via_isa_set_irq(opaque, pin, level); +} + +static void via_isa_request_i8259_irq(void *opaque, int irq, int level) +{ + ViaISAState *s = opaque; + qemu_set_irq(s->cpu_intr, level); } static void via_isa_realize(PCIDevice *d, Error **errp) @@ -648,6 +674,7 @@ static void via_isa_realize(PCIDevice *d, Error **errp) int i; qdev_init_gpio_out(dev, &s->cpu_intr, 1); + qdev_init_gpio_in_named(dev, via_isa_pirq, "pirq", PCI_NUM_PINS); isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d), errp); @@ -661,8 +688,6 @@ static void via_isa_realize(PCIDevice *d, Error **errp) i8254_pit_init(isa_bus, 0x40, 0, NULL); i8257_dma_init(isa_bus, 0); - qdev_init_gpio_in_named(dev, via_isa_set_pci_irq, "pirq", PCI_NUM_PINS); - /* RTC */ qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000); if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) { |