@@ -132,10 +132,10 @@ static void pc_q35_init(ram_addr_t ram_size,
i8259 = i8259_init(cpu_irq[0]);
isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
isa_irq_state->i8259 = i8259;
- isa_irq_state->ioapic = ioapic_init();
isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
- host_bus = gmch_init(&gmch_state, isa_irq, &ich9_lpc_devfn);
+ host_bus = gmch_init(&gmch_state, isa_irq, &ich9_lpc_devfn,
+ &isa_irq_state->ioapic);
isa_bus_irqs(isa_irq);
pc_register_ferr_irq(isa_reserve_irq(13));
@@ -59,6 +59,7 @@ struct ICH9_LPCState {
/* ICH9 LPC PCI to ISA bridge */
PCIDevice d;
+ int apic_mode;
int pci_irq_levels[ICH9_LPC_NB_PIRQS];
APMState apm;
@@ -68,6 +69,7 @@ struct ICH9_LPCState {
struct ICH9_LPCIrqState {
struct ICH9_LPCState *lpc;
qemu_irq *pic;
+ qemu_irq *ioapic;
};
typedef struct GMCHState {
@@ -154,12 +156,42 @@ static void gmch_update_pcixbar(struct GMCH_PCIState *gs)
static struct ICH9_LPCState *ich9_lpc_init(PCIBus *bus, int devfn, struct ICH9_LPCIrqState *irq_state);
static void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
+static void ich9_lpc_ioapic_update_fn(void *opaque, int reset)
+{
+ struct ICH9_LPCIrqState *irq_state = opaque;
+ struct ICH9_LPCState *ich9_lpc = irq_state->lpc;
+
+ /* this can be called via gmch_init() where ich9_lpc isn't
+ allocated yet */
+ if (ich9_lpc == NULL)
+ return;
+
+ if (reset)
+ ich9_lpc->apic_mode = 0;
+ else
+ ich9_lpc->apic_mode = 1;
+}
+
/* return the global irq number corresponding to a given device irq
- pin. We could also use the bus number to have a more precise
- mapping. */
-static int pci_slot_get_pirq(void *opaque, PCIDevice *pci_dev, int irq_num)
+ pin.
+ pic mode: LNKx
+ apic mode: gsi
+ We could also use the bus number/device to have a more precise mapping. */
+static int pci_slot_map_irq(void *opaque, PCIDevice *pci_dev, int irq_num)
{
- return pci_swizzle_map_irq_fn(NULL, pci_dev, irq_num);
+ struct ICH9_LPCIrqState *irq_state = opaque;
+ struct ICH9_LPCState *lpc = irq_state->lpc;
+ int pin = pci_swizzle_map_irq_fn(NULL, pci_dev, irq_num);
+
+ if (!lpc->apic_mode) {
+ /* INTA -> INKA, ...
+ this would be device wise... */
+ return pin;
+ }
+
+ /* APIC mode INTA -> 16, ...
+ this would be device wise... */
+ return pin + ICH9_LPC_PIC_NUM_PINS;
}
/* PAM */
@@ -385,24 +417,30 @@ static int gmch_initfn(PCIDevice *d)
}
/* host bridge */
-PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn)
+PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn,
+ qemu_irq **ioapic)
{
DeviceState *dev;
GMCHState *s;
PCIBus *b;
PCIDevice *d;
struct GMCH_PCIState *gs;
- struct ICH9_LPCIrqState *irq_state = qemu_malloc(sizeof(*irq_state));
+ struct ICH9_LPCIrqState *irq_state = qemu_mallocz(sizeof(*irq_state));
irq_state->pic = pic;
dev = qdev_create(NULL, "gmch-pcihost");
s = gmchstate_from_sysbus(sysbus_from_qdev(dev));
b = pci_register_bus(dev, "pci.0",
- ich9_lpc_set_irq, pci_slot_get_pirq, irq_state, 0,
+ ich9_lpc_set_irq, pci_slot_map_irq, irq_state, 0,
24 /* 24 pin IO APIC */);
s->host.pci.bus = b;
qdev_init(dev);
+ *ioapic = ioapic_init_with_mre(ICH9_LPC_IOAPIC_NUM_PINS,
+ ich9_lpc_ioapic_update_fn,
+ irq_state);
+ irq_state->ioapic = *ioapic;
+
d = pci_create_simple(b, 0, "gmch");
s->dev = d;
gs = DO_UPCAST(struct GMCH_PCIState, d, d);
@@ -554,11 +592,10 @@ static void ich9_lpc_pic_irq(struct ICH9_LPCState *lpc, int irq_num,
abort();
}
-static void ich9_lpc_set_irq(void *opaque, int irq_num, int level)
+/* irq_num = LNKx: 0 = LNKA, ... */
+static void ich9_lpc_set_irq_pic(struct ICH9_LPCState *ich9_lpc,
+ qemu_irq *pic, int irq_num, int level)
{
- struct ICH9_LPCIrqState *irq_state = opaque;
- struct ICH9_LPCState *ich9_lpc = irq_state->lpc;
- qemu_irq *pic = irq_state->pic;
int i, pic_level;
int pic_irq;
int pic_dis;
@@ -569,16 +606,11 @@ static void ich9_lpc_set_irq(void *opaque, int irq_num, int level)
/* now we change the pic irq level according to
the ich9 lpc irq mappings */
ich9_lpc_pic_irq(ich9_lpc, irq_num, &pic_irq, &pic_dis);
+ assert(pic_irq < ICH9_LPC_PIC_NUM_PINS);
if (pic_dis) {
return;
}
- if (pic_irq > 24) {
- fprintf(stderr, "%s: error irq_num %d pic_irq %d\n",
- __func__, irq_num, pic_irq);
- return;
- }
-
/* The pic level is the logical OR of all the PCI irqs mapped to it */
pic_level = 0;
for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
@@ -592,6 +624,27 @@ static void ich9_lpc_set_irq(void *opaque, int irq_num, int level)
qemu_set_irq(pic[pic_irq], pic_level);
}
+/* irq_num = GSI */
+static void ich9_lpc_set_irq_apic(struct ICH9_LPCState *ich9_lpc,
+ qemu_irq *ioapic, int irq_num, int level)
+{
+ assert(irq_num < ICH9_LPC_IOAPIC_NUM_PINS);
+ qemu_set_irq(ioapic[irq_num], level);
+}
+
+/* pic mode: irq_num = LNKx
+ apic mode: irq_num = GSI */
+static void ich9_lpc_set_irq(void *opaque, int irq_num, int level)
+{
+ struct ICH9_LPCIrqState *irq_state = opaque;
+ struct ICH9_LPCState *ich9_lpc = irq_state->lpc;
+
+ if (ich9_lpc->apic_mode)
+ ich9_lpc_set_irq_apic(ich9_lpc, irq_state->ioapic, irq_num, level);
+ else
+ ich9_lpc_set_irq_pic(ich9_lpc, irq_state->pic, irq_num, level);
+}
+
/* APM */
static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
{
@@ -25,7 +25,8 @@
#include "acpi_ich9.h"
void gmch_init_memory_mappings(PCIDevice *d);
-PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn);
+PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn,
+ qemu_irq **ioapic);
PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int secondary_bus_num);
@@ -176,6 +177,9 @@ void ich9_hot_add_init(struct ich9_lpc_pm_regs *pm);
#define ICH9_LPC_RCBA_EN 0x1
#define ICH9_LPC_RCBA_DEFAULT 0x0
+#define ICH9_LPC_PIC_NUM_PINS 16
+#define ICH9_LPC_IOAPIC_NUM_PINS 24
+
/* D30:F1 power management I/O registers
offset from the address ICH9_LPC_PMBASE */
apic mode for pci interrupt routing. Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> --- hw/pc_q35.c | 4 +- hw/q35.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++----------- hw/q35.h | 6 +++- 3 files changed, 77 insertions(+), 20 deletions(-)