* 03/02/19 B. Helgaas Make pcat_compat system-wide, not per-IOSAPIC.
* Remove iosapic_address & gsi_base from external interfaces.
* Rationalize __init/__devinit attributes.
+ * 04/12/04 Ashok Raj <ashok.raj@intel.com> Intel Corporation 2004
+ * Updated to work with irq migration necessary for CPU Hotplug
*/
/*
* Here is what the interrupt logic between a PCI device and the kernel looks like:
#define DBG(fmt...)
#endif
-static spinlock_t iosapic_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(iosapic_lock);
/* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */
static struct iosapic_intr_info {
- char *addr; /* base address of IOSAPIC */
+ char __iomem *addr; /* base address of IOSAPIC */
u32 low32; /* current value of low word of Redirection table entry */
unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */
char rte_index; /* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */
unsigned char dmode : 3; /* delivery mode (see iosapic.h) */
unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */
unsigned char trigger : 1; /* trigger mode (see iosapic.h) */
+ int refcnt; /* reference counter */
} iosapic_intr_info[IA64_NUM_VECTORS];
static struct iosapic {
- char *addr; /* base address of IOSAPIC */
+ char __iomem *addr; /* base address of IOSAPIC */
unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */
unsigned short num_rte; /* number of RTE in this IOSAPIC */
+#ifdef CONFIG_NUMA
+ unsigned short node; /* numa node association via pxm */
+#endif
} iosapic_lists[NR_IOSAPICS];
static int num_iosapic;
static void
set_rte (unsigned int vector, unsigned int dest, int mask)
{
- unsigned long pol, trigger, dmode, flags;
+ unsigned long pol, trigger, dmode;
u32 low32, high32;
- char *addr;
+ char __iomem *addr;
int rte_index;
char redir;
pol = iosapic_intr_info[vector].polarity;
trigger = iosapic_intr_info[vector].trigger;
dmode = iosapic_intr_info[vector].dmode;
+ vector &= (~IA64_IRQ_REDIRECTED);
redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;
+
#ifdef CONFIG_SMP
{
unsigned int irq;
/* dest contains both id and eid */
high32 = (dest << IOSAPIC_DEST_SHIFT);
- spin_lock_irqsave(&iosapic_lock, flags);
- {
- writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT);
- writel(high32, addr + IOSAPIC_WINDOW);
- writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
- writel(low32, addr + IOSAPIC_WINDOW);
- iosapic_intr_info[vector].low32 = low32;
- }
- spin_unlock_irqrestore(&iosapic_lock, flags);
+ iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
+ iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+ iosapic_intr_info[vector].low32 = low32;
}
static void
mask_irq (unsigned int irq)
{
unsigned long flags;
- char *addr;
+ char __iomem *addr;
u32 low32;
int rte_index;
ia64_vector vec = irq_to_vector(irq);
spin_lock_irqsave(&iosapic_lock, flags);
{
- writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
-
/* set only the mask bit */
low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
-
- writel(low32, addr + IOSAPIC_WINDOW);
+ iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
}
spin_unlock_irqrestore(&iosapic_lock, flags);
}
unmask_irq (unsigned int irq)
{
unsigned long flags;
- char *addr;
+ char __iomem *addr;
u32 low32;
int rte_index;
ia64_vector vec = irq_to_vector(irq);
spin_lock_irqsave(&iosapic_lock, flags);
{
- writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
- writel(low32, addr + IOSAPIC_WINDOW);
+ iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
}
spin_unlock_irqrestore(&iosapic_lock, flags);
}
unsigned long flags;
u32 high32, low32;
int dest, rte_index;
- char *addr;
+ char __iomem *addr;
int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
ia64_vector vec;
spin_lock_irqsave(&iosapic_lock, flags);
{
- /* get current delivery mode by reading the low32 */
- writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);
+
if (redir)
/* change delivery mode to lowest priority */
low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);
iosapic_intr_info[vec].low32 = low32;
- writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT);
- writel(high32, addr + IOSAPIC_WINDOW);
- writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
- writel(low32, addr + IOSAPIC_WINDOW);
+ iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
+ iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
}
spin_unlock_irqrestore(&iosapic_lock, flags);
#endif
{
ia64_vector vec = irq_to_vector(irq);
- writel(vec, iosapic_intr_info[vec].addr + IOSAPIC_EOI);
+ move_irq(irq);
+ iosapic_eoi(iosapic_intr_info[vec].addr, vec);
}
#define iosapic_shutdown_level_irq mask_irq
iosapic_ack_edge_irq (unsigned int irq)
{
irq_desc_t *idesc = irq_descp(irq);
+
+ move_irq(irq);
/*
* Once we have recorded IRQ_PENDING already, we can mask the
* interrupt for real. This prevents IRQ storms from unhandled
};
unsigned int
-iosapic_version (char *addr)
+iosapic_version (char __iomem *addr)
{
/*
* IOSAPIC Version Register return 32 bit structure like:
* unsigned int reserved2 : 8;
* }
*/
- writel(IOSAPIC_VERSION, addr + IOSAPIC_REG_SELECT);
- return readl(IOSAPIC_WINDOW + addr);
+ return iosapic_read(addr, IOSAPIC_VERSION);
}
/*
int rte_index;
int index;
unsigned long gsi_base;
- char *iosapic_address;
+ void __iomem *iosapic_address;
index = find_iosapic(gsi);
if (index < 0) {
- printk(KERN_WARNING "%s: No IOSAPIC for GSI 0x%x\n", __FUNCTION__, gsi);
+ printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi);
return;
}
iosapic_intr_info[vector].addr = iosapic_address;
iosapic_intr_info[vector].gsi_base = gsi_base;
iosapic_intr_info[vector].trigger = trigger;
+ iosapic_intr_info[vector].refcnt++;
if (trigger == IOSAPIC_EDGE)
irq_type = &irq_type_iosapic_edge;
}
}
+static unsigned int
+get_target_cpu (unsigned int gsi, int vector)
+{
+#ifdef CONFIG_SMP
+ static int cpu = -1;
+
+ /*
+ * If the platform supports redirection via XTP, let it
+ * distribute interrupts.
+ */
+ if (smp_int_redirect & SMP_IRQ_REDIRECTION)
+ return hard_smp_processor_id();
+
+ /*
+ * Some interrupts (ACPI SCI, for instance) are registered
+ * before the BSP is marked as online.
+ */
+ if (!cpu_online(smp_processor_id()))
+ return hard_smp_processor_id();
+
+#ifdef CONFIG_NUMA
+ {
+ int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0;
+ cpumask_t cpu_mask;
+
+ iosapic_index = find_iosapic(gsi);
+ if (iosapic_index < 0 ||
+ iosapic_lists[iosapic_index].node == MAX_NUMNODES)
+ goto skip_numa_setup;
+
+ cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node);
+
+ for_each_cpu_mask(numa_cpu, cpu_mask) {
+ if (!cpu_online(numa_cpu))
+ cpu_clear(numa_cpu, cpu_mask);
+ }
+
+ num_cpus = cpus_weight(cpu_mask);
+
+ if (!num_cpus)
+ goto skip_numa_setup;
+
+ /* Use vector assigment to distribute across cpus in node */
+ cpu_index = vector % num_cpus;
+
+ for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++)
+ numa_cpu = next_cpu(numa_cpu, cpu_mask);
+
+ if (numa_cpu != NR_CPUS)
+ return cpu_physical_id(numa_cpu);
+ }
+skip_numa_setup:
+#endif
+ /*
+ * Otherwise, round-robin interrupt vectors across all the
+ * processors. (It'd be nice if we could be smarter in the
+ * case of NUMA.)
+ */
+ do {
+ if (++cpu >= NR_CPUS)
+ cpu = 0;
+ } while (!cpu_online(cpu));
+
+ return cpu_physical_id(cpu);
+#else
+ return hard_smp_processor_id();
+#endif
+}
+
/*
* ACPI can describe IOSAPIC interrupts via static tables and namespace
* methods. This provides an interface to register those interrupts and
unsigned long polarity, unsigned long trigger)
{
int vector;
- unsigned int dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
+ unsigned int dest;
+ unsigned long flags;
+
+ /*
+ * If this GSI has already been registered (i.e., it's a
+ * shared interrupt, or we lost a race to register it),
+ * don't touch the RTE.
+ */
+ spin_lock_irqsave(&iosapic_lock, flags);
+ {
+ vector = gsi_to_vector(gsi);
+ if (vector > 0) {
+ iosapic_intr_info[vector].refcnt++;
+ spin_unlock_irqrestore(&iosapic_lock, flags);
+ return vector;
+ }
- vector = gsi_to_vector(gsi);
- if (vector < 0)
vector = assign_irq_vector(AUTO_ASSIGN);
+ dest = get_target_cpu(gsi, vector);
+ register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
+ polarity, trigger);
- register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
- polarity, trigger);
+ set_rte(vector, dest, 1);
+ }
+ spin_unlock_irqrestore(&iosapic_lock, flags);
- printk(KERN_INFO "GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n",
- gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
- (trigger == IOSAPIC_EDGE ? "edge" : "level"), dest, vector);
+ printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
+ gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
+ (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
+ cpu_logical_id(dest), dest, vector);
- /* program the IOSAPIC routing table */
- set_rte(vector, dest, 0);
return vector;
}
+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+iosapic_unregister_intr (unsigned int gsi)
+{
+ unsigned long flags;
+ int irq, vector;
+ irq_desc_t *idesc;
+ int rte_index;
+ unsigned long trigger, polarity;
+
+ /*
+ * If the irq associated with the gsi is not found,
+ * iosapic_unregister_intr() is unbalanced. We need to check
+ * this again after getting locks.
+ */
+ irq = gsi_to_irq(gsi);
+ if (irq < 0) {
+ printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
+ WARN_ON(1);
+ return;
+ }
+ vector = irq_to_vector(irq);
+
+ idesc = irq_descp(irq);
+ spin_lock_irqsave(&idesc->lock, flags);
+ spin_lock(&iosapic_lock);
+ {
+ rte_index = iosapic_intr_info[vector].rte_index;
+ if (rte_index < 0) {
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
+ WARN_ON(1);
+ return;
+ }
+
+ if (--iosapic_intr_info[vector].refcnt > 0) {
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ return;
+ }
+
+ /*
+ * If interrupt handlers still exist on the irq
+ * associated with the gsi, don't unregister the
+ * interrupt.
+ */
+ if (idesc->action) {
+ iosapic_intr_info[vector].refcnt++;
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ printk(KERN_WARNING "Cannot unregister GSI. IRQ %u is still in use.\n", irq);
+ return;
+ }
+
+ /* Clear the interrupt controller descriptor. */
+ idesc->handler = &no_irq_type;
+
+ trigger = iosapic_intr_info[vector].trigger;
+ polarity = iosapic_intr_info[vector].polarity;
+
+ /* Clear the interrupt information. */
+ memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
+ iosapic_intr_info[vector].rte_index = -1; /* mark as unused */
+ }
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+
+ /* Free the interrupt vector */
+ free_irq_vector(vector);
+
+ printk(KERN_INFO "GSI %u (%s, %s) -> vector %d unregisterd.\n",
+ gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
+ (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
+ vector);
+}
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
+
/*
* ACPI calls this when it finds an entry for a platform interrupt.
* Note that the irq_base and IOSAPIC address must be set in iosapic_init().
int iosapic_vector, u16 eid, u16 id,
unsigned long polarity, unsigned long trigger)
{
+ static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"};
unsigned char delivery;
- int vector;
+ int vector, mask = 0;
unsigned int dest = ((id << 8) | eid) & 0xffff;
switch (int_type) {
case ACPI_INTERRUPT_CPEI:
vector = IA64_CPE_VECTOR;
delivery = IOSAPIC_LOWEST_PRIORITY;
+ mask = 1;
break;
default:
- printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type\n");
+ printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type);
return -1;
}
- register_intr(gsi, vector, delivery, polarity,
- trigger);
+ register_intr(gsi, vector, delivery, polarity, trigger);
- printk(KERN_INFO "PLATFORM int 0x%x: GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n",
- int_type, gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
- (trigger == IOSAPIC_EDGE ? "edge" : "level"), dest, vector);
+ printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
+ int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown",
+ int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
+ (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
+ cpu_logical_id(dest), dest, vector);
- /* program the IOSAPIC routing table */
- set_rte(vector, dest, 0);
+ set_rte(vector, dest, mask);
return vector;
}
unsigned long trigger)
{
int vector;
- unsigned int dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
+ unsigned int dest = hard_smp_processor_id();
vector = isa_irq_to_vector(isa_irq);
register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);
- DBG("ISA: IRQ %u -> GSI 0x%x (%s,%s) -> CPU 0x%04x vector %d\n",
- isa_irq, gsi, polarity == IOSAPIC_POL_HIGH ? "high" : "low",
- trigger == IOSAPIC_EDGE ? "edge" : "level", dest, vector);
+ DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n",
+ isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level",
+ polarity == IOSAPIC_POL_HIGH ? "high" : "low",
+ cpu_logical_id(dest), dest, vector);
- /* program the IOSAPIC routing table */
- set_rte(vector, dest, 0);
+ set_rte(vector, dest, 1);
}
void __init
{
int num_rte;
unsigned int isa_irq, ver;
- char *addr;
+ char __iomem *addr;
addr = ioremap(phys_addr, 0);
ver = iosapic_version(addr);
iosapic_lists[num_iosapic].addr = addr;
iosapic_lists[num_iosapic].gsi_base = gsi_base;
iosapic_lists[num_iosapic].num_rte = num_rte;
+#ifdef CONFIG_NUMA
+ iosapic_lists[num_iosapic].node = MAX_NUMNODES;
+#endif
num_iosapic++;
if ((gsi_base == 0) && pcat_compat) {
}
}
-void
-iosapic_enable_intr (unsigned int vector)
-{
- unsigned int dest;
- irq_desc_t *desc;
-
- /*
- * In the case of a shared interrupt, do not re-route the vector, and
- * especially do not mask a running interrupt (startup will not get
- * called for a shared interrupt).
- */
- desc = irq_descp(vector);
- if (desc->action)
- return;
-
-#ifdef CONFIG_SMP
- /*
- * For platforms that do not support interrupt redirect via the XTP interface, we
- * can round-robin the PCI device interrupts to the processors
- */
- if (!(smp_int_redirect & SMP_IRQ_REDIRECTION)) {
- static int cpu_index = -1;
-
- do
- if (++cpu_index >= NR_CPUS)
- cpu_index = 0;
- while (!cpu_online(cpu_index));
-
- dest = cpu_physical_id(cpu_index) & 0xffff;
- } else {
- /*
- * Direct the interrupt vector to the current cpu, platform redirection
- * will distribute them.
- */
- dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
- }
-#else
- /* direct the interrupt vector to the running cpu id */
- dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
-#endif
- set_rte(vector, dest, 1);
-
- printk(KERN_INFO "IOSAPIC: vector %d -> CPU 0x%04x, enabled\n",
- vector, dest);
-}
-
-#ifdef CONFIG_ACPI_PCI
-
+#ifdef CONFIG_NUMA
void __init
-iosapic_parse_prt (void)
+map_iosapic_to_node(unsigned int gsi_base, int node)
{
- struct acpi_prt_entry *entry;
- struct list_head *node;
- unsigned int gsi;
- int vector;
- char pci_id[16];
- struct hw_interrupt_type *irq_type = &irq_type_iosapic_level;
- irq_desc_t *idesc;
-
- list_for_each(node, &acpi_prt.entries) {
- entry = list_entry(node, struct acpi_prt_entry, node);
-
- /* We're only interested in static (non-link) entries. */
- if (entry->link.handle)
- continue;
-
- gsi = entry->link.index;
-
- vector = gsi_to_vector(gsi);
- if (vector < 0) {
- if (find_iosapic(gsi) < 0)
- continue;
-
- /* allocate a vector for this interrupt line */
- if (pcat_compat && (gsi < 16))
- vector = isa_irq_to_vector(gsi);
- else
- /* new GSI; allocate a vector for it */
- vector = assign_irq_vector(AUTO_ASSIGN);
-
- register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW,
- IOSAPIC_LEVEL);
- }
- entry->irq = vector;
- snprintf(pci_id, sizeof(pci_id), "%02x:%02x:%02x[%c]",
- entry->id.segment, entry->id.bus, entry->id.device, 'A' + entry->pin);
-
- /*
- * If vector was previously initialized to a different
- * handler, re-initialize.
- */
- idesc = irq_descp(vector);
- if (idesc->handler != irq_type)
- register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW,
- IOSAPIC_LEVEL);
+ int index;
+ index = find_iosapic(gsi_base);
+ if (index < 0) {
+ printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n",
+ __FUNCTION__, gsi_base);
+ return;
}
+ iosapic_lists[index].node = node;
+ return;
}
-
-#endif /* CONFIG_ACPI */
+#endif