|
@@ -57,7 +57,7 @@ int nr_ioapic_registers[MAX_IO_APICS];
|
|
|
* Rough estimation of how many shared IRQs there are, can
|
|
|
* be changed anytime.
|
|
|
*/
|
|
|
-#define MAX_PLUS_SHARED_IRQS NR_IRQS
|
|
|
+#define MAX_PLUS_SHARED_IRQS NR_IRQ_VECTORS
|
|
|
#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)
|
|
|
|
|
|
/*
|
|
@@ -85,6 +85,7 @@ int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1};
|
|
|
int pin; \
|
|
|
struct irq_pin_list *entry = irq_2_pin + irq; \
|
|
|
\
|
|
|
+ BUG_ON(irq >= NR_IRQS); \
|
|
|
for (;;) { \
|
|
|
unsigned int reg; \
|
|
|
pin = entry->pin; \
|
|
@@ -127,6 +128,8 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+static u8 gsi_2_irq[NR_IRQ_VECTORS] = { [0 ... NR_IRQ_VECTORS-1] = 0xFF };
|
|
|
+
|
|
|
/*
|
|
|
* The common case is 1:1 IRQ<->pin mappings. Sometimes there are
|
|
|
* shared ISA-space IRQs, so we have to support them. We are super
|
|
@@ -137,6 +140,7 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin)
|
|
|
static int first_free_entry = NR_IRQS;
|
|
|
struct irq_pin_list *entry = irq_2_pin + irq;
|
|
|
|
|
|
+ BUG_ON(irq >= NR_IRQS);
|
|
|
while (entry->next)
|
|
|
entry = irq_2_pin + entry->next;
|
|
|
|
|
@@ -144,7 +148,7 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin)
|
|
|
entry->next = first_free_entry;
|
|
|
entry = irq_2_pin + entry->next;
|
|
|
if (++first_free_entry >= PIN_MAP_SIZE)
|
|
|
- panic("io_apic.c: whoops");
|
|
|
+ panic("io_apic.c: ran out of irq_2_pin entries!");
|
|
|
}
|
|
|
entry->apic = apic;
|
|
|
entry->pin = pin;
|
|
@@ -420,6 +424,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
|
|
|
best_guess = irq;
|
|
|
}
|
|
|
}
|
|
|
+ BUG_ON(best_guess >= NR_IRQS);
|
|
|
return best_guess;
|
|
|
}
|
|
|
|
|
@@ -610,6 +615,64 @@ static inline int irq_trigger(int idx)
|
|
|
return MPBIOS_trigger(idx);
|
|
|
}
|
|
|
|
|
|
+static int next_irq = 16;
|
|
|
+
|
|
|
+/*
|
|
|
+ * gsi_irq_sharing -- Name overload! "irq" can be either a legacy IRQ
|
|
|
+ * in the range 0-15, a linux IRQ in the range 0-223, or a GSI number
|
|
|
+ * from ACPI, which can reach 800 in large boxen.
|
|
|
+ *
|
|
|
+ * Compact the sparse GSI space into a sequential IRQ series and reuse
|
|
|
+ * vectors if possible.
|
|
|
+ */
|
|
|
+int gsi_irq_sharing(int gsi)
|
|
|
+{
|
|
|
+ int i, tries, vector;
|
|
|
+
|
|
|
+ BUG_ON(gsi >= NR_IRQ_VECTORS);
|
|
|
+
|
|
|
+ if (platform_legacy_irq(gsi))
|
|
|
+ return gsi;
|
|
|
+
|
|
|
+ if (gsi_2_irq[gsi] != 0xFF)
|
|
|
+ return (int)gsi_2_irq[gsi];
|
|
|
+
|
|
|
+ tries = NR_IRQS;
|
|
|
+ try_again:
|
|
|
+ vector = assign_irq_vector(gsi);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Sharing vectors means sharing IRQs, so scan irq_vectors for previous
|
|
|
+ * use of vector and if found, return that IRQ. However, we never want
|
|
|
+ * to share legacy IRQs, which usually have a different trigger mode
|
|
|
+ * than PCI.
|
|
|
+ */
|
|
|
+ for (i = 0; i < NR_IRQS; i++)
|
|
|
+ if (IO_APIC_VECTOR(i) == vector)
|
|
|
+ break;
|
|
|
+ if (platform_legacy_irq(i)) {
|
|
|
+ if (--tries >= 0) {
|
|
|
+ IO_APIC_VECTOR(i) = 0;
|
|
|
+ goto try_again;
|
|
|
+ }
|
|
|
+ panic("gsi_irq_sharing: didn't find an IRQ using vector 0x%02X for GSI %d", vector, gsi);
|
|
|
+ }
|
|
|
+ if (i < NR_IRQS) {
|
|
|
+ gsi_2_irq[gsi] = i;
|
|
|
+ printk(KERN_INFO "GSI %d sharing vector 0x%02X and IRQ %d\n",
|
|
|
+ gsi, vector, i);
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+
|
|
|
+ i = next_irq++;
|
|
|
+ BUG_ON(i >= NR_IRQS);
|
|
|
+ gsi_2_irq[gsi] = i;
|
|
|
+ IO_APIC_VECTOR(i) = vector;
|
|
|
+ printk(KERN_INFO "GSI %d assigned vector 0x%02X and IRQ %d\n",
|
|
|
+ gsi, vector, i);
|
|
|
+ return i;
|
|
|
+}
|
|
|
+
|
|
|
static int pin_2_irq(int idx, int apic, int pin)
|
|
|
{
|
|
|
int irq, i;
|
|
@@ -639,6 +702,7 @@ static int pin_2_irq(int idx, int apic, int pin)
|
|
|
while (i < apic)
|
|
|
irq += nr_ioapic_registers[i++];
|
|
|
irq += pin;
|
|
|
+ irq = gsi_irq_sharing(irq);
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
@@ -648,6 +712,7 @@ static int pin_2_irq(int idx, int apic, int pin)
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ BUG_ON(irq >= NR_IRQS);
|
|
|
|
|
|
/*
|
|
|
* PCI IRQ command line redirection. Yes, limits are hardcoded.
|
|
@@ -663,6 +728,7 @@ static int pin_2_irq(int idx, int apic, int pin)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ BUG_ON(irq >= NR_IRQS);
|
|
|
return irq;
|
|
|
}
|
|
|
|
|
@@ -690,8 +756,8 @@ int assign_irq_vector(int irq)
|
|
|
{
|
|
|
static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
|
|
|
|
|
|
- BUG_ON(irq >= NR_IRQ_VECTORS);
|
|
|
- if (IO_APIC_VECTOR(irq) > 0)
|
|
|
+ BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS);
|
|
|
+ if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0)
|
|
|
return IO_APIC_VECTOR(irq);
|
|
|
next:
|
|
|
current_vector += 8;
|
|
@@ -699,9 +765,8 @@ next:
|
|
|
goto next;
|
|
|
|
|
|
if (current_vector >= FIRST_SYSTEM_VECTOR) {
|
|
|
- offset++;
|
|
|
- if (!(offset%8))
|
|
|
- return -ENOSPC;
|
|
|
+ /* If we run out of vectors on large boxen, must share them. */
|
|
|
+ offset = (offset + 1) % 8;
|
|
|
current_vector = FIRST_DEVICE_VECTOR + offset;
|
|
|
}
|
|
|
|
|
@@ -1917,6 +1982,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
|
|
|
entry.polarity = active_high_low;
|
|
|
entry.mask = 1; /* Disabled (masked) */
|
|
|
|
|
|
+ irq = gsi_irq_sharing(irq);
|
|
|
/*
|
|
|
* IRQs < 16 are already in the irq_2_pin[] map
|
|
|
*/
|