summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/include/asm/io_apic.h21
-rw-r--r--arch/x86/kernel/acpi/boot.c8
-rw-r--r--arch/x86/kernel/apic/io_apic.c158
4 files changed, 147 insertions, 41 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 9df5c45..147a7b7 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -836,6 +836,7 @@ config X86_IO_APIC
def_bool y
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_IOAPIC || PCI_MSI
select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+ select IRQ_DOMAIN
config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
bool "Reroute for broken boot IRQs"
diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h
index 978e51f..64c6e34 100644
--- a/arch/x86/include/asm/io_apic.h
+++ b/arch/x86/include/asm/io_apic.h
@@ -98,6 +98,8 @@ struct IR_IO_APIC_route_entry {
#define IOAPIC_AUTO -1
#define IOAPIC_EDGE 0
#define IOAPIC_LEVEL 1
+#define IOAPIC_MAP_ALLOC 0x1
+#define IOAPIC_MAP_CHECK 0x2
#ifdef CONFIG_X86_IO_APIC
@@ -163,6 +165,21 @@ extern int restore_ioapic_entries(void);
extern void setup_ioapic_ids_from_mpc(void);
extern void setup_ioapic_ids_from_mpc_nocheck(void);
+enum ioapic_domain_type {
+ IOAPIC_DOMAIN_INVALID,
+ IOAPIC_DOMAIN_LEGACY,
+ IOAPIC_DOMAIN_STRICT,
+ IOAPIC_DOMAIN_DYNAMIC,
+};
+
+struct device_node;
+struct irq_domain_ops;
+struct ioapic_domain_cfg {
+ enum ioapic_domain_type type;
+ const struct irq_domain_ops *ops;
+ struct device_node *dev;
+};
+
struct mp_ioapic_gsi{
u32 gsi_base;
u32 gsi_end;
@@ -172,7 +189,7 @@ extern u32 gsi_top;
extern int mp_find_ioapic(u32 gsi);
extern int mp_find_ioapic_pin(int ioapic, u32 gsi);
extern u32 mp_pin_to_gsi(int ioapic, int pin);
-extern int mp_map_gsi_to_irq(u32 gsi);
+extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags);
extern void __init mp_register_ioapic(int id, u32 address, u32 gsi_base);
extern void __init pre_init_apic_IRQ0(void);
@@ -215,7 +232,7 @@ static inline void ioapic_insert_resources(void) { }
#define gsi_top (NR_IRQS_LEGACY)
static inline int mp_find_ioapic(u32 gsi) { return 0; }
static inline u32 mp_pin_to_gsi(int ioapic, int pin) { return UINT_MAX; }
-static inline int mp_map_gsi_to_irq(u32 gsi) { return gsi; }
+static inline int mp_map_gsi_to_irq(u32 gsi, unsigned int flags) { return gsi; }
struct io_apic_irq_attr;
static inline int io_apic_set_pci_routing(struct device *dev, int irq,
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 0cf311c..d6635ba 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -100,7 +100,7 @@ static u32 isa_irq_to_gsi[NR_IRQS_LEGACY] __read_mostly = {
#define ACPI_INVALID_GSI INT_MIN
-static int map_gsi_to_irq(unsigned int gsi)
+static int map_gsi_to_irq(unsigned int gsi, unsigned int flags)
{
int i;
@@ -108,7 +108,7 @@ static int map_gsi_to_irq(unsigned int gsi)
if (isa_irq_to_gsi[i] == gsi)
return i;
- return mp_map_gsi_to_irq(gsi);
+ return mp_map_gsi_to_irq(gsi, flags);
}
/*
@@ -417,7 +417,7 @@ static int mp_register_gsi(struct device *dev, u32 gsi, int trigger,
if (acpi_gbl_FADT.sci_interrupt == gsi)
return gsi;
- irq = map_gsi_to_irq(gsi);
+ irq = map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC);
if (irq < 0)
return irq;
@@ -608,7 +608,7 @@ void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger)
int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
{
- int irq = map_gsi_to_irq(gsi);
+ int irq = map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC | IOAPIC_MAP_CHECK);
if (irq >= 0) {
#ifdef CONFIG_X86_IO_APIC
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 7fd9f1b..51ce80004 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -31,6 +31,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/syscore_ops.h>
+#include <linux/irqdomain.h>
#include <linux/msi.h>
#include <linux/htirq.h>
#include <linux/freezer.h>
@@ -83,6 +84,7 @@ int sis_apic_bug = -1;
static DEFINE_RAW_SPINLOCK(ioapic_lock);
static DEFINE_RAW_SPINLOCK(vector_lock);
+static DEFINE_MUTEX(ioapic_mutex);
static struct ioapic {
/*
@@ -97,6 +99,8 @@ static struct ioapic {
struct mpc_ioapic mp_config;
/* IO APIC gsi routing info */
struct mp_ioapic_gsi gsi_config;
+ struct ioapic_domain_cfg irqdomain_cfg;
+ struct irq_domain *irqdomain;
DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1);
} ioapics[MAX_IO_APICS];
@@ -142,6 +146,11 @@ static inline int mp_init_irq_at_boot(int ioapic, int irq)
return ioapic == 0 || (irq >= 0 && irq < nr_legacy_irqs());
}
+static inline struct irq_domain *mp_ioapic_irqdomain(int ioapic)
+{
+ return ioapics[ioapic].irqdomain;
+}
+
int nr_ioapics;
/* The one past the highest gsi number used */
@@ -959,19 +968,79 @@ static int irq_trigger(int idx)
return trigger;
}
-int mp_map_gsi_to_irq(u32 gsi)
+static int alloc_irq_from_domain(struct irq_domain *domain, u32 gsi, int pin)
{
+ int irq = -1;
+ int ioapic = (int)(long)domain->host_data;
+ int type = ioapics[ioapic].irqdomain_cfg.type;
+
+ switch (type) {
+ case IOAPIC_DOMAIN_LEGACY:
+ /*
+ * Dynamically allocate IRQ number for non-ISA IRQs in the first 16
+ * GSIs on some weird platforms.
+ */
+ if (gsi < nr_legacy_irqs())
+ irq = irq_create_mapping(domain, pin);
+ else if (irq_create_strict_mappings(domain, gsi, pin, 1) == 0)
+ irq = gsi;
+ break;
+ case IOAPIC_DOMAIN_STRICT:
+ if (irq_create_strict_mappings(domain, gsi, pin, 1) == 0)
+ irq = gsi;
+ break;
+ case IOAPIC_DOMAIN_DYNAMIC:
+ irq = irq_create_mapping(domain, pin);
+ break;
+ default:
+ WARN(1, "ioapic: unknown irqdomain type %d\n", type);
+ break;
+ }
+
+ return irq > 0 ? irq : -1;
+}
+
+static int mp_map_pin_to_irq(u32 gsi, int idx, int ioapic, int pin,
+ unsigned int flags)
+{
+ int irq;
+ struct irq_domain *domain = mp_ioapic_irqdomain(ioapic);
+
/*
- * Provide an identity mapping of gsi == irq except on truly weird
- * platforms that have non isa irqs in the first 16 gsis.
+ * Don't use irqdomain to manage ISA IRQs because there may be
+ * multiple IOAPIC pins sharing the same ISA IRQ number and
+ * irqdomain only supports 1:1 mapping between IOAPIC pin and
+ * IRQ number. A typical IOAPIC has 24 pins, pin 0-15 are used
+ * for legacy IRQs and pin 16-23 are used for PCI IRQs (PIRQ A-H).
+ * When ACPI is disabled, only legacy IRQ numbers (IRQ0-15) are
+ * available, and some BIOSes may use MP Interrupt Source records
+ * to override IRQ numbers for PIRQs instead of reprogramming
+ * the interrupt routing logic. Thus there may be multiple pins
+ * sharing the same legacy IRQ number when ACPI is disabled.
*/
- return gsi >= nr_legacy_irqs() ? gsi : gsi_top + gsi;
+ if (idx >= 0 && test_bit(mp_irqs[idx].srcbus, mp_bus_not_pci))
+ return mp_irqs[idx].srcbusirq;
+
+ if (!domain) {
+ /*
+ * Provide an identity mapping of gsi == irq except on truly
+ * weird platforms that have non isa irqs in the first 16 gsis.
+ */
+ return gsi >= nr_legacy_irqs() ? gsi : gsi_top + gsi;
+ }
+
+ mutex_lock(&ioapic_mutex);
+ irq = irq_find_mapping(domain, pin);
+ if (irq <= 0 && (flags & IOAPIC_MAP_ALLOC))
+ irq = alloc_irq_from_domain(domain, gsi, pin);
+ mutex_unlock(&ioapic_mutex);
+
+ return irq > 0 ? irq : -1;
}
-static int pin_2_irq(int idx, int apic, int pin)
+static int pin_2_irq(int idx, int ioapic, int pin, unsigned int flags)
{
- int irq;
- int bus = mp_irqs[idx].srcbus;
+ u32 gsi = mp_pin_to_gsi(ioapic, pin);
/*
* Debugging check, we are in big trouble if this message pops up!
@@ -989,7 +1058,7 @@ static int pin_2_irq(int idx, int apic, int pin)
apic_printk(APIC_VERBOSE, KERN_DEBUG
"disabling PIRQ%d\n", pin-16);
} else {
- irq = pirq_entries[pin-16];
+ int irq = pirq_entries[pin-16];
apic_printk(APIC_VERBOSE, KERN_DEBUG
"using PIRQ%d -> IRQ %d\n",
pin-16, irq);
@@ -999,12 +1068,23 @@ static int pin_2_irq(int idx, int apic, int pin)
}
#endif
- if (test_bit(bus, mp_bus_not_pci))
- irq = mp_irqs[idx].srcbusirq;
- else
- irq = mp_map_gsi_to_irq(mp_pin_to_gsi(apic, pin));
+ return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
+}
- return irq;
+int mp_map_gsi_to_irq(u32 gsi, unsigned int flags)
+{
+ int ioapic, pin, idx;
+
+ ioapic = mp_find_ioapic(gsi);
+ if (ioapic < 0)
+ return -1;
+
+ pin = mp_find_ioapic_pin(ioapic, gsi);
+ idx = find_irq_entry(ioapic, pin, mp_INT);
+ if ((flags & IOAPIC_MAP_CHECK) && idx < 0)
+ return -1;
+
+ return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
}
/*
@@ -1014,7 +1094,7 @@ static int pin_2_irq(int idx, int apic, int pin)
int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin,
struct io_apic_irq_attr *irq_attr)
{
- int irq, i, best_guess = -1;
+ int irq, i, best_ioapic = -1, best_idx = -1;
apic_printk(APIC_DEBUG,
"querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n",
@@ -1043,30 +1123,37 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin,
continue;
/* Skip ISA IRQs */
- irq = pin_2_irq(i, ioapic_idx, mp_irqs[i].dstirq);
- if (ioapic_idx == 0 && !IO_APIC_IRQ(irq))
+ irq = pin_2_irq(i, ioapic_idx, mp_irqs[i].dstirq, 0);
+ if (irq > 0 && !IO_APIC_IRQ(irq))
continue;
if (pin == (mp_irqs[i].srcbusirq & 3)) {
- set_io_apic_irq_attr(irq_attr, ioapic_idx,
- mp_irqs[i].dstirq,
- irq_trigger(i),
- irq_polarity(i));
- return irq;
+ best_idx = i;
+ best_ioapic = ioapic_idx;
+ goto out;
}
+
/*
* Use the first all-but-pin matching entry as a
* best-guess fuzzy result for broken mptables.
*/
- if (best_guess < 0) {
- set_io_apic_irq_attr(irq_attr, ioapic_idx,
- mp_irqs[i].dstirq,
- irq_trigger(i),
- irq_polarity(i));
- best_guess = irq;
+ if (best_idx < 0) {
+ best_idx = i;
+ best_ioapic = ioapic_idx;
}
}
- return best_guess;
+ if (best_idx < 0)
+ return -1;
+
+out:
+ irq = pin_2_irq(best_idx, best_ioapic, mp_irqs[best_idx].dstirq,
+ IOAPIC_MAP_ALLOC);
+ if (irq > 0)
+ set_io_apic_irq_attr(irq_attr, best_ioapic,
+ mp_irqs[best_idx].dstirq,
+ irq_trigger(best_idx),
+ irq_polarity(best_idx));
+ return irq;
}
EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
@@ -1257,7 +1344,7 @@ static inline int IO_APIC_irq_trigger(int irq)
for_each_ioapic_pin(apic, pin) {
idx = find_irq_entry(apic, pin, mp_INT);
- if ((idx != -1) && (irq == pin_2_irq(idx, apic, pin)))
+ if ((idx != -1) && (irq == pin_2_irq(idx, apic, pin, 0)))
return irq_trigger(idx);
}
/*
@@ -1383,8 +1470,9 @@ static void __init __io_apic_setup_irqs(unsigned int ioapic_idx)
if (io_apic_pin_not_connected(idx, ioapic_idx, pin))
continue;
- irq = pin_2_irq(idx, ioapic_idx, pin);
- if (!mp_init_irq_at_boot(ioapic_idx, irq))
+ irq = pin_2_irq(idx, ioapic_idx, pin,
+ ioapic_idx ? 0 : IOAPIC_MAP_ALLOC);
+ if (irq < 0 || !mp_init_irq_at_boot(ioapic_idx, irq))
continue;
/*
@@ -1434,8 +1522,8 @@ void setup_IO_APIC_irq_extra(u32 gsi)
if (idx == -1)
return;
- irq = pin_2_irq(idx, ioapic_idx, pin);
- if (mp_init_irq_at_boot(ioapic_idx, irq))
+ irq = pin_2_irq(idx, ioapic_idx, pin, IOAPIC_MAP_ALLOC);
+ if (irq < 0 || mp_init_irq_at_boot(ioapic_idx, irq))
return;
set_io_apic_irq_attr(&attr, ioapic_idx, pin, irq_trigger(idx),
@@ -3543,8 +3631,8 @@ void __init setup_ioapic_dest(void)
if (irq_entry == -1)
continue;
- irq = pin_2_irq(irq_entry, ioapic, pin);
- if (!mp_init_irq_at_boot(ioapic, irq))
+ irq = pin_2_irq(irq_entry, ioapic, pin, 0);
+ if (irq < 0 || !mp_init_irq_at_boot(ioapic, irq))
continue;
idata = irq_get_irq_data(irq);
OpenPOWER on IntegriCloud