aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arch/x86/boot/mpspec.c101
-rw-r--r--src/devices/device_util.c4
-rw-r--r--src/include/device/device.h9
-rw-r--r--src/include/device/path.h7
4 files changed, 120 insertions, 1 deletions
diff --git a/src/arch/x86/boot/mpspec.c b/src/arch/x86/boot/mpspec.c
index e7d767b7ac..44236f1cca 100644
--- a/src/arch/x86/boot/mpspec.c
+++ b/src/arch/x86/boot/mpspec.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <arch/cpu.h>
#include <cpu/x86/lapic.h>
+#include <drivers/generic/ioapic/chip.h>
/* Initialize the specified "mc" struct with initial values. */
void mptable_init(struct mp_config_table *mc, u32 lapic_addr)
@@ -410,3 +411,103 @@ void *mptable_finalize(struct mp_config_table *mc)
printk(BIOS_DEBUG, "Wrote the mp table end at: %p - %p\n", mc, smp_next_mpe_entry(mc));
return smp_next_mpe_entry(mc);
}
+
+unsigned long __attribute__((weak)) write_smp_table(unsigned long addr)
+{
+ struct drivers_generic_ioapic_config *ioapic_config;
+ struct mp_config_table *mc;
+ int isa_bus, pin, parentpin;
+ device_t dev, parent, oldparent;
+ void *tmp, *v;
+ int isaioapic = -1;
+
+ v = smp_write_floating_table(addr, 0);
+ mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN);
+
+ mptable_init(mc, LOCAL_APIC_ADDR);
+
+ smp_write_processors(mc);
+
+ mptable_write_buses(mc, NULL, &isa_bus);
+
+ for(dev = all_devices; dev; dev = dev->next) {
+ if (dev->path.type != DEVICE_PATH_IOAPIC)
+ continue;
+
+ if (!(ioapic_config = dev->chip_info)) {
+ printk(BIOS_ERR, "%s has no config, ignoring\n", dev_path(dev));
+ continue;
+ }
+ smp_write_ioapic(mc, dev->path.ioapic.ioapic_id,
+ ioapic_config->version,
+ ioapic_config->base);
+
+ if (ioapic_config->have_isa_interrupts) {
+ if (isaioapic > 1)
+ printk(BIOS_ERR, "More than one IOAPIC with ISA interrupts?\n");
+ else
+ isaioapic = dev->path.ioapic.ioapic_id;;
+ }
+ }
+
+ if (isaioapic >= 0) {
+ /* Legacy Interrupts */
+ printk(BIOS_DEBUG, "writing ISA IRQs\n");
+ mptable_add_isa_interrupts(mc, isa_bus, isaioapic, 0);
+ }
+
+ for(dev = all_devices; dev; dev = dev->next) {
+
+ if (dev->path.type != DEVICE_PATH_PCI)
+ continue;
+
+ pin = (dev->path.pci.devfn & 7) % 4;
+
+ if (dev->pci_irq_info[pin].ioapic_dst_id) {
+ printk(BIOS_DEBUG, "fixed IRQ entry for: %s: INT%c# -> IOAPIC %d PIN %d\n", dev_path(dev),
+ pin + 'A',
+ dev->pci_irq_info[pin].ioapic_dst_id,
+ dev->pci_irq_info[pin].ioapic_irq_pin);
+ smp_write_intsrc(mc, mp_INT,
+ dev->pci_irq_info[pin].ioapic_flags,
+ dev->bus->secondary,
+ ((dev->path.pci.devfn & 0xf8) >> 1) | pin,
+ dev->pci_irq_info[pin].ioapic_dst_id,
+ dev->pci_irq_info[pin].ioapic_irq_pin);
+ } else {
+ oldparent = parent = dev;
+ while((parent = parent->bus->dev)) {
+ parentpin = (oldparent->path.pci.devfn >> 3) + (oldparent->path.pci.devfn & 7);
+ parentpin += dev->path.pci.devfn & 7;
+ parentpin += dev->path.pci.devfn >> 3;
+ parentpin %= 4;
+
+ if (parent->pci_irq_info[parentpin].ioapic_dst_id) {
+ printk(BIOS_DEBUG, "automatic IRQ entry for %s: INT%c# -> IOAPIC %d PIN %d\n",
+ dev_path(dev), pin + 'A',
+ parent->pci_irq_info[parentpin].ioapic_dst_id,
+ parent->pci_irq_info[parentpin].ioapic_irq_pin);
+ smp_write_intsrc(mc, mp_INT,
+ parent->pci_irq_info[parentpin].ioapic_flags,
+ dev->bus->secondary,
+ ((dev->path.pci.devfn & 0xf8) >> 1) | pin,
+ parent->pci_irq_info[parentpin].ioapic_dst_id,
+ parent->pci_irq_info[parentpin].ioapic_irq_pin);
+
+ break;
+ }
+
+ if (parent->path.type == DEVICE_PATH_PCI_DOMAIN) {
+ printk(BIOS_WARNING, "no IRQ found for %s\n", dev_path(dev));
+ break;
+ }
+ oldparent = parent;
+ }
+ }
+ }
+
+ mptable_lintsrc(mc, isa_bus);
+ tmp = mptable_finalize(mc);
+ printk(BIOS_INFO, "MPTABLE len: %d\n", (unsigned int)tmp - (unsigned int)v);
+ return (unsigned long)tmp;
+}
diff --git a/src/devices/device_util.c b/src/devices/device_util.c
index ecc2c13659..7c1cde9895 100644
--- a/src/devices/device_util.c
+++ b/src/devices/device_util.c
@@ -201,6 +201,10 @@ const char *dev_path(device_t dev)
sprintf(buffer, "APIC: %02x",
dev->path.apic.apic_id);
break;
+ case DEVICE_PATH_IOAPIC:
+ sprintf(buffer, "IOAPIC: %02x",
+ dev->path.ioapic.ioapic_id);
+ break;
case DEVICE_PATH_PCI_DOMAIN:
sprintf(buffer, "PCI_DOMAIN: %04x",
dev->path.pci_domain.domain);
diff --git a/src/include/device/device.h b/src/include/device/device.h
index 0aea1d6087..f36710ae0a 100644
--- a/src/include/device/device.h
+++ b/src/include/device/device.h
@@ -61,6 +61,13 @@ struct bus {
* combination:
*/
+struct pci_irq_info {
+ unsigned int ioapic_irq_pin;
+ unsigned int ioapic_src_pin;
+ unsigned int ioapic_dst_id;
+ unsigned int ioapic_flags;
+};
+
struct device {
struct bus * bus; /* bus this device is on, for bridge
* devices, it is the up stream bus */
@@ -77,7 +84,7 @@ struct device {
unsigned int enabled : 1; /* set if we should enable the device */
unsigned int initialized : 1; /* set if we have initialized the device */
unsigned int on_mainboard : 1;
-
+ struct pci_irq_info pci_irq_info[4];
u8 command;
/* Base registers for this device. I/O, MEM and Expansion ROM */
diff --git a/src/include/device/path.h b/src/include/device/path.h
index 018fb9313f..3dc7625029 100644
--- a/src/include/device/path.h
+++ b/src/include/device/path.h
@@ -12,6 +12,7 @@ enum device_path_type {
DEVICE_PATH_APIC_CLUSTER,
DEVICE_PATH_CPU,
DEVICE_PATH_CPU_BUS,
+ DEVICE_PATH_IOAPIC,
};
struct pci_domain_path
@@ -43,6 +44,11 @@ struct apic_path
unsigned index;
};
+struct ioapic_path
+{
+ unsigned ioapic_id;
+};
+
struct apic_cluster_path
{
unsigned cluster;
@@ -66,6 +72,7 @@ struct device_path {
struct pnp_path pnp;
struct i2c_path i2c;
struct apic_path apic;
+ struct ioapic_path ioapic;
struct pci_domain_path pci_domain;
struct apic_cluster_path apic_cluster;
struct cpu_path cpu;