xref: /openbmc/linux/arch/ia64/kernel/iosapic.c (revision 0e888adc)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * I/O SAPIC support.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 1999 Intel Corp.
51da177e4SLinus Torvalds  * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com>
61da177e4SLinus Torvalds  * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com>
71da177e4SLinus Torvalds  * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co.
81da177e4SLinus Torvalds  *	David Mosberger-Tang <davidm@hpl.hp.com>
91da177e4SLinus Torvalds  * Copyright (C) 1999 VA Linux Systems
101da177e4SLinus Torvalds  * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * 00/04/19	D. Mosberger	Rewritten to mirror more closely the x86 I/O APIC code.
131da177e4SLinus Torvalds  *				In particular, we now have separate handlers for edge
141da177e4SLinus Torvalds  *				and level triggered interrupts.
151da177e4SLinus Torvalds  * 00/10/27	Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation
161da177e4SLinus Torvalds  *				PCI to vector mapping, shared PCI interrupts.
171da177e4SLinus Torvalds  * 00/10/27	D. Mosberger	Document things a bit more to make them more understandable.
181da177e4SLinus Torvalds  *				Clean up much of the old IOSAPIC cruft.
191da177e4SLinus Torvalds  * 01/07/27	J.I. Lee	PCI irq routing, Platform/Legacy interrupts and fixes for
201da177e4SLinus Torvalds  *				ACPI S5(SoftOff) support.
211da177e4SLinus Torvalds  * 02/01/23	J.I. Lee	iosapic pgm fixes for PCI irq routing from _PRT
221da177e4SLinus Torvalds  * 02/01/07     E. Focht        <efocht@ess.nec.de> Redirectable interrupt vectors in
231da177e4SLinus Torvalds  *                              iosapic_set_affinity(), initializations for
241da177e4SLinus Torvalds  *                              /proc/irq/#/smp_affinity
251da177e4SLinus Torvalds  * 02/04/02	P. Diefenbaugh	Cleaned up ACPI PCI IRQ routing.
261da177e4SLinus Torvalds  * 02/04/18	J.I. Lee	bug fix in iosapic_init_pci_irq
271da177e4SLinus Torvalds  * 02/04/30	J.I. Lee	bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping
281da177e4SLinus Torvalds  *				error
291da177e4SLinus Torvalds  * 02/07/29	T. Kochi	Allocate interrupt vectors dynamically
301da177e4SLinus Torvalds  * 02/08/04	T. Kochi	Cleaned up terminology (irq, global system interrupt, vector, etc.)
311da177e4SLinus Torvalds  * 02/09/20	D. Mosberger	Simplified by taking advantage of ACPI's pci_irq code.
321da177e4SLinus Torvalds  * 03/02/19	B. Helgaas	Make pcat_compat system-wide, not per-IOSAPIC.
331da177e4SLinus Torvalds  *				Remove iosapic_address & gsi_base from external interfaces.
341da177e4SLinus Torvalds  *				Rationalize __init/__devinit attributes.
351da177e4SLinus Torvalds  * 04/12/04 Ashok Raj	<ashok.raj@intel.com> Intel Corporation 2004
361da177e4SLinus Torvalds  *				Updated to work with irq migration necessary for CPU Hotplug
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * Here is what the interrupt logic between a PCI device and the kernel looks like:
401da177e4SLinus Torvalds  *
411da177e4SLinus Torvalds  * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD).  The
421da177e4SLinus Torvalds  *     device is uniquely identified by its bus--, and slot-number (the function
431da177e4SLinus Torvalds  *     number does not matter here because all functions share the same interrupt
441da177e4SLinus Torvalds  *     lines).
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller.
471da177e4SLinus Torvalds  *     Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level
481da177e4SLinus Torvalds  *     triggered and use the same polarity).  Each interrupt line has a unique Global
491da177e4SLinus Torvalds  *     System Interrupt (GSI) number which can be calculated as the sum of the controller's
501da177e4SLinus Torvalds  *     base GSI number and the IOSAPIC pin number to which the line connects.
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin
531da177e4SLinus Torvalds  *     into the IA-64 interrupt vector.  This interrupt vector is then sent to the CPU.
541da177e4SLinus Torvalds  *
551da177e4SLinus Torvalds  * (4) The kernel recognizes an interrupt as an IRQ.  The IRQ interface is used as
561da177e4SLinus Torvalds  *     architecture-independent interrupt handling mechanism in Linux.  As an
571da177e4SLinus Torvalds  *     IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number
581da177e4SLinus Torvalds  *     mapping.  On smaller systems, we use one-to-one mapping between IA-64 vector and
591da177e4SLinus Torvalds  *     IRQ.  A platform can implement platform_irq_to_vector(irq) and
601da177e4SLinus Torvalds  *     platform_local_vector_to_irq(vector) APIs to differentiate the mapping.
611da177e4SLinus Torvalds  *     Please see also include/asm-ia64/hw_irq.h for those APIs.
621da177e4SLinus Torvalds  *
631da177e4SLinus Torvalds  * To sum up, there are three levels of mappings involved:
641da177e4SLinus Torvalds  *
651da177e4SLinus Torvalds  *	PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ
661da177e4SLinus Torvalds  *
671da177e4SLinus Torvalds  * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts.
681da177e4SLinus Torvalds  * Now we use "IRQ" only for Linux IRQ's.  ISA IRQ (isa_irq) is the only exception in this
691da177e4SLinus Torvalds  * source code.
701da177e4SLinus Torvalds  */
711da177e4SLinus Torvalds #include <linux/config.h>
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds #include <linux/acpi.h>
741da177e4SLinus Torvalds #include <linux/init.h>
751da177e4SLinus Torvalds #include <linux/irq.h>
761da177e4SLinus Torvalds #include <linux/kernel.h>
771da177e4SLinus Torvalds #include <linux/list.h>
781da177e4SLinus Torvalds #include <linux/pci.h>
791da177e4SLinus Torvalds #include <linux/smp.h>
801da177e4SLinus Torvalds #include <linux/smp_lock.h>
811da177e4SLinus Torvalds #include <linux/string.h>
8224eeb568SKenji Kaneshige #include <linux/bootmem.h>
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds #include <asm/delay.h>
851da177e4SLinus Torvalds #include <asm/hw_irq.h>
861da177e4SLinus Torvalds #include <asm/io.h>
871da177e4SLinus Torvalds #include <asm/iosapic.h>
881da177e4SLinus Torvalds #include <asm/machvec.h>
891da177e4SLinus Torvalds #include <asm/processor.h>
901da177e4SLinus Torvalds #include <asm/ptrace.h>
911da177e4SLinus Torvalds #include <asm/system.h>
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds #undef DEBUG_INTERRUPT_ROUTING
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds #ifdef DEBUG_INTERRUPT_ROUTING
971da177e4SLinus Torvalds #define DBG(fmt...)	printk(fmt)
981da177e4SLinus Torvalds #else
991da177e4SLinus Torvalds #define DBG(fmt...)
1001da177e4SLinus Torvalds #endif
1011da177e4SLinus Torvalds 
10224eeb568SKenji Kaneshige #define NR_PREALLOCATE_RTE_ENTRIES	(PAGE_SIZE / sizeof(struct iosapic_rte_info))
10324eeb568SKenji Kaneshige #define RTE_PREALLOCATED	(1)
10424eeb568SKenji Kaneshige 
1051da177e4SLinus Torvalds static DEFINE_SPINLOCK(iosapic_lock);
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */
1081da177e4SLinus Torvalds 
10924eeb568SKenji Kaneshige struct iosapic_rte_info {
11024eeb568SKenji Kaneshige 	struct list_head rte_list;	/* node in list of RTEs sharing the same vector */
1111da177e4SLinus Torvalds 	char __iomem	*addr;		/* base address of IOSAPIC */
1121da177e4SLinus Torvalds 	unsigned int	gsi_base;	/* first GSI assigned to this IOSAPIC */
11324eeb568SKenji Kaneshige 	char		rte_index;	/* IOSAPIC RTE index */
11424eeb568SKenji Kaneshige 	int		refcnt;		/* reference counter */
11524eeb568SKenji Kaneshige 	unsigned int	flags;		/* flags */
11624eeb568SKenji Kaneshige } ____cacheline_aligned;
11724eeb568SKenji Kaneshige 
11824eeb568SKenji Kaneshige static struct iosapic_intr_info {
11924eeb568SKenji Kaneshige 	struct list_head rtes;		/* RTEs using this vector (empty => not an IOSAPIC interrupt) */
12024eeb568SKenji Kaneshige 	int		count;		/* # of RTEs that shares this vector */
12124eeb568SKenji Kaneshige 	u32		low32;		/* current value of low word of Redirection table entry */
12224eeb568SKenji Kaneshige 	unsigned int	dest;		/* destination CPU physical ID */
1231da177e4SLinus Torvalds 	unsigned char	dmode	: 3;	/* delivery mode (see iosapic.h) */
1241da177e4SLinus Torvalds 	unsigned char 	polarity: 1;	/* interrupt polarity (see iosapic.h) */
1251da177e4SLinus Torvalds 	unsigned char	trigger	: 1;	/* trigger mode (see iosapic.h) */
1261da177e4SLinus Torvalds } iosapic_intr_info[IA64_NUM_VECTORS];
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds static struct iosapic {
1291da177e4SLinus Torvalds 	char __iomem	*addr;		/* base address of IOSAPIC */
1301da177e4SLinus Torvalds 	unsigned int 	gsi_base;	/* first GSI assigned to this IOSAPIC */
1311da177e4SLinus Torvalds 	unsigned short 	num_rte;	/* number of RTE in this IOSAPIC */
1320e888adcSKenji Kaneshige 	int		rtes_inuse;	/* # of RTEs in use on this IOSAPIC */
1331da177e4SLinus Torvalds #ifdef CONFIG_NUMA
1341da177e4SLinus Torvalds 	unsigned short	node;		/* numa node association via pxm */
1351da177e4SLinus Torvalds #endif
1361da177e4SLinus Torvalds } iosapic_lists[NR_IOSAPICS];
1371da177e4SLinus Torvalds 
1380e888adcSKenji Kaneshige static unsigned char pcat_compat __devinitdata;	/* 8259 compatibility flag */
1391da177e4SLinus Torvalds 
14024eeb568SKenji Kaneshige static int iosapic_kmalloc_ok;
14124eeb568SKenji Kaneshige static LIST_HEAD(free_rte_list);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds /*
1441da177e4SLinus Torvalds  * Find an IOSAPIC associated with a GSI
1451da177e4SLinus Torvalds  */
1461da177e4SLinus Torvalds static inline int
1471da177e4SLinus Torvalds find_iosapic (unsigned int gsi)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds 	int i;
1501da177e4SLinus Torvalds 
1510e888adcSKenji Kaneshige 	for (i = 0; i < NR_IOSAPICS; i++) {
1521da177e4SLinus Torvalds 		if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte)
1531da177e4SLinus Torvalds 			return i;
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	return -1;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds static inline int
1601da177e4SLinus Torvalds _gsi_to_vector (unsigned int gsi)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	struct iosapic_intr_info *info;
16324eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)
16624eeb568SKenji Kaneshige 		list_for_each_entry(rte, &info->rtes, rte_list)
16724eeb568SKenji Kaneshige 			if (rte->gsi_base + rte->rte_index == gsi)
1681da177e4SLinus Torvalds 				return info - iosapic_intr_info;
1691da177e4SLinus Torvalds 	return -1;
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds /*
1731da177e4SLinus Torvalds  * Translate GSI number to the corresponding IA-64 interrupt vector.  If no
1741da177e4SLinus Torvalds  * entry exists, return -1.
1751da177e4SLinus Torvalds  */
1761da177e4SLinus Torvalds inline int
1771da177e4SLinus Torvalds gsi_to_vector (unsigned int gsi)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	return _gsi_to_vector(gsi);
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds int
1831da177e4SLinus Torvalds gsi_to_irq (unsigned int gsi)
1841da177e4SLinus Torvalds {
18524eeb568SKenji Kaneshige 	unsigned long flags;
18624eeb568SKenji Kaneshige 	int irq;
1871da177e4SLinus Torvalds 	/*
1881da177e4SLinus Torvalds 	 * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq
1891da177e4SLinus Torvalds 	 * numbers...
1901da177e4SLinus Torvalds 	 */
19124eeb568SKenji Kaneshige 	spin_lock_irqsave(&iosapic_lock, flags);
19224eeb568SKenji Kaneshige 	{
19324eeb568SKenji Kaneshige 		irq = _gsi_to_vector(gsi);
19424eeb568SKenji Kaneshige 	}
19524eeb568SKenji Kaneshige 	spin_unlock_irqrestore(&iosapic_lock, flags);
19624eeb568SKenji Kaneshige 
19724eeb568SKenji Kaneshige 	return irq;
19824eeb568SKenji Kaneshige }
19924eeb568SKenji Kaneshige 
20024eeb568SKenji Kaneshige static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec)
20124eeb568SKenji Kaneshige {
20224eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
20324eeb568SKenji Kaneshige 
20424eeb568SKenji Kaneshige 	list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
20524eeb568SKenji Kaneshige 		if (rte->gsi_base + rte->rte_index == gsi)
20624eeb568SKenji Kaneshige 			return rte;
20724eeb568SKenji Kaneshige 	return NULL;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds static void
21124eeb568SKenji Kaneshige set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds 	unsigned long pol, trigger, dmode;
2141da177e4SLinus Torvalds 	u32 low32, high32;
2151da177e4SLinus Torvalds 	char __iomem *addr;
2161da177e4SLinus Torvalds 	int rte_index;
2171da177e4SLinus Torvalds 	char redir;
21824eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);
2211da177e4SLinus Torvalds 
22224eeb568SKenji Kaneshige 	rte = gsi_vector_to_rte(gsi, vector);
22324eeb568SKenji Kaneshige 	if (!rte)
2241da177e4SLinus Torvalds 		return;		/* not an IOSAPIC interrupt */
2251da177e4SLinus Torvalds 
22624eeb568SKenji Kaneshige 	rte_index = rte->rte_index;
22724eeb568SKenji Kaneshige 	addr	= rte->addr;
2281da177e4SLinus Torvalds 	pol     = iosapic_intr_info[vector].polarity;
2291da177e4SLinus Torvalds 	trigger = iosapic_intr_info[vector].trigger;
2301da177e4SLinus Torvalds 	dmode   = iosapic_intr_info[vector].dmode;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds #ifdef CONFIG_SMP
2351da177e4SLinus Torvalds 	{
2361da177e4SLinus Torvalds 		unsigned int irq;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 		for (irq = 0; irq < NR_IRQS; ++irq)
2391da177e4SLinus Torvalds 			if (irq_to_vector(irq) == vector) {
2401da177e4SLinus Torvalds 				set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);
2411da177e4SLinus Torvalds 				break;
2421da177e4SLinus Torvalds 			}
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds #endif
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	low32 = ((pol << IOSAPIC_POLARITY_SHIFT) |
2471da177e4SLinus Torvalds 		 (trigger << IOSAPIC_TRIGGER_SHIFT) |
2481da177e4SLinus Torvalds 		 (dmode << IOSAPIC_DELIVERY_SHIFT) |
2491da177e4SLinus Torvalds 		 ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) |
2501da177e4SLinus Torvalds 		 vector);
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	/* dest contains both id and eid */
2531da177e4SLinus Torvalds 	high32 = (dest << IOSAPIC_DEST_SHIFT);
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
2561da177e4SLinus Torvalds 	iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
2571da177e4SLinus Torvalds 	iosapic_intr_info[vector].low32 = low32;
25824eeb568SKenji Kaneshige 	iosapic_intr_info[vector].dest = dest;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds static void
2621da177e4SLinus Torvalds nop (unsigned int vector)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	/* do nothing... */
2651da177e4SLinus Torvalds }
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds static void
2681da177e4SLinus Torvalds mask_irq (unsigned int irq)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	unsigned long flags;
2711da177e4SLinus Torvalds 	char __iomem *addr;
2721da177e4SLinus Torvalds 	u32 low32;
2731da177e4SLinus Torvalds 	int rte_index;
2741da177e4SLinus Torvalds 	ia64_vector vec = irq_to_vector(irq);
27524eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
2761da177e4SLinus Torvalds 
27724eeb568SKenji Kaneshige 	if (list_empty(&iosapic_intr_info[vec].rtes))
2781da177e4SLinus Torvalds 		return;			/* not an IOSAPIC interrupt! */
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	spin_lock_irqsave(&iosapic_lock, flags);
2811da177e4SLinus Torvalds 	{
2821da177e4SLinus Torvalds 		/* set only the mask bit */
2831da177e4SLinus Torvalds 		low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
28424eeb568SKenji Kaneshige 		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
28524eeb568SKenji Kaneshige 			addr = rte->addr;
28624eeb568SKenji Kaneshige 			rte_index = rte->rte_index;
2871da177e4SLinus Torvalds 			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
2881da177e4SLinus Torvalds 		}
28924eeb568SKenji Kaneshige 	}
2901da177e4SLinus Torvalds 	spin_unlock_irqrestore(&iosapic_lock, flags);
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds static void
2941da177e4SLinus Torvalds unmask_irq (unsigned int irq)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	unsigned long flags;
2971da177e4SLinus Torvalds 	char __iomem *addr;
2981da177e4SLinus Torvalds 	u32 low32;
2991da177e4SLinus Torvalds 	int rte_index;
3001da177e4SLinus Torvalds 	ia64_vector vec = irq_to_vector(irq);
30124eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
3021da177e4SLinus Torvalds 
30324eeb568SKenji Kaneshige 	if (list_empty(&iosapic_intr_info[vec].rtes))
3041da177e4SLinus Torvalds 		return;			/* not an IOSAPIC interrupt! */
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	spin_lock_irqsave(&iosapic_lock, flags);
3071da177e4SLinus Torvalds 	{
3081da177e4SLinus Torvalds 		low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
30924eeb568SKenji Kaneshige 		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
31024eeb568SKenji Kaneshige 			addr = rte->addr;
31124eeb568SKenji Kaneshige 			rte_index = rte->rte_index;
3121da177e4SLinus Torvalds 			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
3131da177e4SLinus Torvalds 		}
31424eeb568SKenji Kaneshige 	}
3151da177e4SLinus Torvalds 	spin_unlock_irqrestore(&iosapic_lock, flags);
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds static void
3201da177e4SLinus Torvalds iosapic_set_affinity (unsigned int irq, cpumask_t mask)
3211da177e4SLinus Torvalds {
3221da177e4SLinus Torvalds #ifdef CONFIG_SMP
3231da177e4SLinus Torvalds 	unsigned long flags;
3241da177e4SLinus Torvalds 	u32 high32, low32;
3251da177e4SLinus Torvalds 	int dest, rte_index;
3261da177e4SLinus Torvalds 	char __iomem *addr;
3271da177e4SLinus Torvalds 	int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
3281da177e4SLinus Torvalds 	ia64_vector vec;
32924eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	irq &= (~IA64_IRQ_REDIRECTED);
3321da177e4SLinus Torvalds 	vec = irq_to_vector(irq);
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	if (cpus_empty(mask))
3351da177e4SLinus Torvalds 		return;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	dest = cpu_physical_id(first_cpu(mask));
3381da177e4SLinus Torvalds 
33924eeb568SKenji Kaneshige 	if (list_empty(&iosapic_intr_info[vec].rtes))
3401da177e4SLinus Torvalds 		return;			/* not an IOSAPIC interrupt */
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	set_irq_affinity_info(irq, dest, redir);
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 	/* dest contains both id and eid */
3451da177e4SLinus Torvalds 	high32 = dest << IOSAPIC_DEST_SHIFT;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	spin_lock_irqsave(&iosapic_lock, flags);
3481da177e4SLinus Torvalds 	{
3491da177e4SLinus Torvalds 		low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 		if (redir)
3521da177e4SLinus Torvalds 		        /* change delivery mode to lowest priority */
3531da177e4SLinus Torvalds 			low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
3541da177e4SLinus Torvalds 		else
3551da177e4SLinus Torvalds 		        /* change delivery mode to fixed */
3561da177e4SLinus Torvalds 			low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 		iosapic_intr_info[vec].low32 = low32;
35924eeb568SKenji Kaneshige 		iosapic_intr_info[vec].dest = dest;
36024eeb568SKenji Kaneshige 		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
36124eeb568SKenji Kaneshige 			addr = rte->addr;
36224eeb568SKenji Kaneshige 			rte_index = rte->rte_index;
3631da177e4SLinus Torvalds 			iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
3641da177e4SLinus Torvalds 			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
3651da177e4SLinus Torvalds 		}
36624eeb568SKenji Kaneshige 	}
3671da177e4SLinus Torvalds 	spin_unlock_irqrestore(&iosapic_lock, flags);
3681da177e4SLinus Torvalds #endif
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds /*
3721da177e4SLinus Torvalds  * Handlers for level-triggered interrupts.
3731da177e4SLinus Torvalds  */
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds static unsigned int
3761da177e4SLinus Torvalds iosapic_startup_level_irq (unsigned int irq)
3771da177e4SLinus Torvalds {
3781da177e4SLinus Torvalds 	unmask_irq(irq);
3791da177e4SLinus Torvalds 	return 0;
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds static void
3831da177e4SLinus Torvalds iosapic_end_level_irq (unsigned int irq)
3841da177e4SLinus Torvalds {
3851da177e4SLinus Torvalds 	ia64_vector vec = irq_to_vector(irq);
38624eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	move_irq(irq);
38924eeb568SKenji Kaneshige 	list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
39024eeb568SKenji Kaneshige 		iosapic_eoi(rte->addr, vec);
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds #define iosapic_shutdown_level_irq	mask_irq
3941da177e4SLinus Torvalds #define iosapic_enable_level_irq	unmask_irq
3951da177e4SLinus Torvalds #define iosapic_disable_level_irq	mask_irq
3961da177e4SLinus Torvalds #define iosapic_ack_level_irq		nop
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds struct hw_interrupt_type irq_type_iosapic_level = {
3991da177e4SLinus Torvalds 	.typename =	"IO-SAPIC-level",
4001da177e4SLinus Torvalds 	.startup =	iosapic_startup_level_irq,
4011da177e4SLinus Torvalds 	.shutdown =	iosapic_shutdown_level_irq,
4021da177e4SLinus Torvalds 	.enable =	iosapic_enable_level_irq,
4031da177e4SLinus Torvalds 	.disable =	iosapic_disable_level_irq,
4041da177e4SLinus Torvalds 	.ack =		iosapic_ack_level_irq,
4051da177e4SLinus Torvalds 	.end =		iosapic_end_level_irq,
4061da177e4SLinus Torvalds 	.set_affinity =	iosapic_set_affinity
4071da177e4SLinus Torvalds };
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds /*
4101da177e4SLinus Torvalds  * Handlers for edge-triggered interrupts.
4111da177e4SLinus Torvalds  */
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds static unsigned int
4141da177e4SLinus Torvalds iosapic_startup_edge_irq (unsigned int irq)
4151da177e4SLinus Torvalds {
4161da177e4SLinus Torvalds 	unmask_irq(irq);
4171da177e4SLinus Torvalds 	/*
4181da177e4SLinus Torvalds 	 * IOSAPIC simply drops interrupts pended while the
4191da177e4SLinus Torvalds 	 * corresponding pin was masked, so we can't know if an
4201da177e4SLinus Torvalds 	 * interrupt is pending already.  Let's hope not...
4211da177e4SLinus Torvalds 	 */
4221da177e4SLinus Torvalds 	return 0;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds static void
4261da177e4SLinus Torvalds iosapic_ack_edge_irq (unsigned int irq)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	irq_desc_t *idesc = irq_descp(irq);
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	move_irq(irq);
4311da177e4SLinus Torvalds 	/*
4321da177e4SLinus Torvalds 	 * Once we have recorded IRQ_PENDING already, we can mask the
4331da177e4SLinus Torvalds 	 * interrupt for real. This prevents IRQ storms from unhandled
4341da177e4SLinus Torvalds 	 * devices.
4351da177e4SLinus Torvalds 	 */
4361da177e4SLinus Torvalds 	if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED))
4371da177e4SLinus Torvalds 		mask_irq(irq);
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds #define iosapic_enable_edge_irq		unmask_irq
4411da177e4SLinus Torvalds #define iosapic_disable_edge_irq	nop
4421da177e4SLinus Torvalds #define iosapic_end_edge_irq		nop
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds struct hw_interrupt_type irq_type_iosapic_edge = {
4451da177e4SLinus Torvalds 	.typename =	"IO-SAPIC-edge",
4461da177e4SLinus Torvalds 	.startup =	iosapic_startup_edge_irq,
4471da177e4SLinus Torvalds 	.shutdown =	iosapic_disable_edge_irq,
4481da177e4SLinus Torvalds 	.enable =	iosapic_enable_edge_irq,
4491da177e4SLinus Torvalds 	.disable =	iosapic_disable_edge_irq,
4501da177e4SLinus Torvalds 	.ack =		iosapic_ack_edge_irq,
4511da177e4SLinus Torvalds 	.end =		iosapic_end_edge_irq,
4521da177e4SLinus Torvalds 	.set_affinity =	iosapic_set_affinity
4531da177e4SLinus Torvalds };
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds unsigned int
4561da177e4SLinus Torvalds iosapic_version (char __iomem *addr)
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds 	/*
4591da177e4SLinus Torvalds 	 * IOSAPIC Version Register return 32 bit structure like:
4601da177e4SLinus Torvalds 	 * {
4611da177e4SLinus Torvalds 	 *	unsigned int version   : 8;
4621da177e4SLinus Torvalds 	 *	unsigned int reserved1 : 8;
4631da177e4SLinus Torvalds 	 *	unsigned int max_redir : 8;
4641da177e4SLinus Torvalds 	 *	unsigned int reserved2 : 8;
4651da177e4SLinus Torvalds 	 * }
4661da177e4SLinus Torvalds 	 */
4671da177e4SLinus Torvalds 	return iosapic_read(addr, IOSAPIC_VERSION);
4681da177e4SLinus Torvalds }
4691da177e4SLinus Torvalds 
47024eeb568SKenji Kaneshige static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol)
47124eeb568SKenji Kaneshige {
47224eeb568SKenji Kaneshige 	int i, vector = -1, min_count = -1;
47324eeb568SKenji Kaneshige 	struct iosapic_intr_info *info;
47424eeb568SKenji Kaneshige 
47524eeb568SKenji Kaneshige 	/*
47624eeb568SKenji Kaneshige 	 * shared vectors for edge-triggered interrupts are not
47724eeb568SKenji Kaneshige 	 * supported yet
47824eeb568SKenji Kaneshige 	 */
47924eeb568SKenji Kaneshige 	if (trigger == IOSAPIC_EDGE)
48024eeb568SKenji Kaneshige 		return -1;
48124eeb568SKenji Kaneshige 
48224eeb568SKenji Kaneshige 	for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) {
48324eeb568SKenji Kaneshige 		info = &iosapic_intr_info[i];
48424eeb568SKenji Kaneshige 		if (info->trigger == trigger && info->polarity == pol &&
48524eeb568SKenji Kaneshige 		    (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) {
48624eeb568SKenji Kaneshige 			if (min_count == -1 || info->count < min_count) {
48724eeb568SKenji Kaneshige 				vector = i;
48824eeb568SKenji Kaneshige 				min_count = info->count;
48924eeb568SKenji Kaneshige 			}
49024eeb568SKenji Kaneshige 		}
49124eeb568SKenji Kaneshige 	}
49224eeb568SKenji Kaneshige 	if (vector < 0)
49324eeb568SKenji Kaneshige 		panic("%s: out of interrupt vectors!\n", __FUNCTION__);
49424eeb568SKenji Kaneshige 
49524eeb568SKenji Kaneshige 	return vector;
49624eeb568SKenji Kaneshige }
49724eeb568SKenji Kaneshige 
4981da177e4SLinus Torvalds /*
4991da177e4SLinus Torvalds  * if the given vector is already owned by other,
5001da177e4SLinus Torvalds  *  assign a new vector for the other and make the vector available
5011da177e4SLinus Torvalds  */
5021da177e4SLinus Torvalds static void __init
5031da177e4SLinus Torvalds iosapic_reassign_vector (int vector)
5041da177e4SLinus Torvalds {
5051da177e4SLinus Torvalds 	int new_vector;
5061da177e4SLinus Torvalds 
50724eeb568SKenji Kaneshige 	if (!list_empty(&iosapic_intr_info[vector].rtes)) {
5081da177e4SLinus Torvalds 		new_vector = assign_irq_vector(AUTO_ASSIGN);
5091da177e4SLinus Torvalds 		printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
5101da177e4SLinus Torvalds 		memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
5111da177e4SLinus Torvalds 		       sizeof(struct iosapic_intr_info));
51224eeb568SKenji Kaneshige 		INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes);
51324eeb568SKenji Kaneshige 		list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes);
5141da177e4SLinus Torvalds 		memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
51524eeb568SKenji Kaneshige 		iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
51624eeb568SKenji Kaneshige 		INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);
5171da177e4SLinus Torvalds 	}
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds 
52024eeb568SKenji Kaneshige static struct iosapic_rte_info *iosapic_alloc_rte (void)
52124eeb568SKenji Kaneshige {
52224eeb568SKenji Kaneshige 	int i;
52324eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
52424eeb568SKenji Kaneshige 	int preallocated = 0;
52524eeb568SKenji Kaneshige 
52624eeb568SKenji Kaneshige 	if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) {
52724eeb568SKenji Kaneshige 		rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);
52824eeb568SKenji Kaneshige 		if (!rte)
52924eeb568SKenji Kaneshige 			return NULL;
53024eeb568SKenji Kaneshige 		for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++)
53124eeb568SKenji Kaneshige 			list_add(&rte->rte_list, &free_rte_list);
53224eeb568SKenji Kaneshige 	}
53324eeb568SKenji Kaneshige 
53424eeb568SKenji Kaneshige 	if (!list_empty(&free_rte_list)) {
53524eeb568SKenji Kaneshige 		rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list);
53624eeb568SKenji Kaneshige 		list_del(&rte->rte_list);
53724eeb568SKenji Kaneshige 		preallocated++;
53824eeb568SKenji Kaneshige 	} else {
53924eeb568SKenji Kaneshige 		rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC);
54024eeb568SKenji Kaneshige 		if (!rte)
54124eeb568SKenji Kaneshige 			return NULL;
54224eeb568SKenji Kaneshige 	}
54324eeb568SKenji Kaneshige 
54424eeb568SKenji Kaneshige 	memset(rte, 0, sizeof(struct iosapic_rte_info));
54524eeb568SKenji Kaneshige 	if (preallocated)
54624eeb568SKenji Kaneshige 		rte->flags |= RTE_PREALLOCATED;
54724eeb568SKenji Kaneshige 
54824eeb568SKenji Kaneshige 	return rte;
54924eeb568SKenji Kaneshige }
55024eeb568SKenji Kaneshige 
55124eeb568SKenji Kaneshige static void iosapic_free_rte (struct iosapic_rte_info *rte)
55224eeb568SKenji Kaneshige {
55324eeb568SKenji Kaneshige 	if (rte->flags & RTE_PREALLOCATED)
55424eeb568SKenji Kaneshige 		list_add_tail(&rte->rte_list, &free_rte_list);
55524eeb568SKenji Kaneshige 	else
55624eeb568SKenji Kaneshige 		kfree(rte);
55724eeb568SKenji Kaneshige }
55824eeb568SKenji Kaneshige 
55924eeb568SKenji Kaneshige static inline int vector_is_shared (int vector)
56024eeb568SKenji Kaneshige {
56124eeb568SKenji Kaneshige 	return (iosapic_intr_info[vector].count > 1);
56224eeb568SKenji Kaneshige }
56324eeb568SKenji Kaneshige 
5641da177e4SLinus Torvalds static void
5651da177e4SLinus Torvalds register_intr (unsigned int gsi, int vector, unsigned char delivery,
5661da177e4SLinus Torvalds 	       unsigned long polarity, unsigned long trigger)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds 	irq_desc_t *idesc;
5691da177e4SLinus Torvalds 	struct hw_interrupt_type *irq_type;
5701da177e4SLinus Torvalds 	int rte_index;
5711da177e4SLinus Torvalds 	int index;
5721da177e4SLinus Torvalds 	unsigned long gsi_base;
5731da177e4SLinus Torvalds 	void __iomem *iosapic_address;
57424eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	index = find_iosapic(gsi);
5771da177e4SLinus Torvalds 	if (index < 0) {
5781da177e4SLinus Torvalds 		printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi);
5791da177e4SLinus Torvalds 		return;
5801da177e4SLinus Torvalds 	}
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 	iosapic_address = iosapic_lists[index].addr;
5831da177e4SLinus Torvalds 	gsi_base = iosapic_lists[index].gsi_base;
5841da177e4SLinus Torvalds 
58524eeb568SKenji Kaneshige 	rte = gsi_vector_to_rte(gsi, vector);
58624eeb568SKenji Kaneshige 	if (!rte) {
58724eeb568SKenji Kaneshige 		rte = iosapic_alloc_rte();
58824eeb568SKenji Kaneshige 		if (!rte) {
58924eeb568SKenji Kaneshige 			printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__);
59024eeb568SKenji Kaneshige 			return;
59124eeb568SKenji Kaneshige 		}
59224eeb568SKenji Kaneshige 
5931da177e4SLinus Torvalds 		rte_index = gsi - gsi_base;
59424eeb568SKenji Kaneshige 		rte->rte_index	= rte_index;
59524eeb568SKenji Kaneshige 		rte->addr	= iosapic_address;
59624eeb568SKenji Kaneshige 		rte->gsi_base	= gsi_base;
59724eeb568SKenji Kaneshige 		rte->refcnt++;
59824eeb568SKenji Kaneshige 		list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes);
59924eeb568SKenji Kaneshige 		iosapic_intr_info[vector].count++;
6000e888adcSKenji Kaneshige 		iosapic_lists[index].rtes_inuse++;
60124eeb568SKenji Kaneshige 	}
60224eeb568SKenji Kaneshige 	else if (vector_is_shared(vector)) {
60324eeb568SKenji Kaneshige 		struct iosapic_intr_info *info = &iosapic_intr_info[vector];
60424eeb568SKenji Kaneshige 		if (info->trigger != trigger || info->polarity != polarity) {
60524eeb568SKenji Kaneshige 			printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__);
60624eeb568SKenji Kaneshige 			return;
60724eeb568SKenji Kaneshige 		}
60824eeb568SKenji Kaneshige 	}
60924eeb568SKenji Kaneshige 
6101da177e4SLinus Torvalds 	iosapic_intr_info[vector].polarity = polarity;
6111da177e4SLinus Torvalds 	iosapic_intr_info[vector].dmode    = delivery;
6121da177e4SLinus Torvalds 	iosapic_intr_info[vector].trigger  = trigger;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	if (trigger == IOSAPIC_EDGE)
6151da177e4SLinus Torvalds 		irq_type = &irq_type_iosapic_edge;
6161da177e4SLinus Torvalds 	else
6171da177e4SLinus Torvalds 		irq_type = &irq_type_iosapic_level;
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	idesc = irq_descp(vector);
6201da177e4SLinus Torvalds 	if (idesc->handler != irq_type) {
6211da177e4SLinus Torvalds 		if (idesc->handler != &no_irq_type)
6221da177e4SLinus Torvalds 			printk(KERN_WARNING "%s: changing vector %d from %s to %s\n",
6231da177e4SLinus Torvalds 			       __FUNCTION__, vector, idesc->handler->typename, irq_type->typename);
6241da177e4SLinus Torvalds 		idesc->handler = irq_type;
6251da177e4SLinus Torvalds 	}
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds static unsigned int
6291da177e4SLinus Torvalds get_target_cpu (unsigned int gsi, int vector)
6301da177e4SLinus Torvalds {
6311da177e4SLinus Torvalds #ifdef CONFIG_SMP
6321da177e4SLinus Torvalds 	static int cpu = -1;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	/*
63524eeb568SKenji Kaneshige 	 * In case of vector shared by multiple RTEs, all RTEs that
63624eeb568SKenji Kaneshige 	 * share the vector need to use the same destination CPU.
63724eeb568SKenji Kaneshige 	 */
63824eeb568SKenji Kaneshige 	if (!list_empty(&iosapic_intr_info[vector].rtes))
63924eeb568SKenji Kaneshige 		return iosapic_intr_info[vector].dest;
64024eeb568SKenji Kaneshige 
64124eeb568SKenji Kaneshige 	/*
6421da177e4SLinus Torvalds 	 * If the platform supports redirection via XTP, let it
6431da177e4SLinus Torvalds 	 * distribute interrupts.
6441da177e4SLinus Torvalds 	 */
6451da177e4SLinus Torvalds 	if (smp_int_redirect & SMP_IRQ_REDIRECTION)
6461da177e4SLinus Torvalds 		return cpu_physical_id(smp_processor_id());
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	/*
6491da177e4SLinus Torvalds 	 * Some interrupts (ACPI SCI, for instance) are registered
6501da177e4SLinus Torvalds 	 * before the BSP is marked as online.
6511da177e4SLinus Torvalds 	 */
6521da177e4SLinus Torvalds 	if (!cpu_online(smp_processor_id()))
6531da177e4SLinus Torvalds 		return cpu_physical_id(smp_processor_id());
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds #ifdef CONFIG_NUMA
6561da177e4SLinus Torvalds 	{
6571da177e4SLinus Torvalds 		int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0;
6581da177e4SLinus Torvalds 		cpumask_t cpu_mask;
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds 		iosapic_index = find_iosapic(gsi);
6611da177e4SLinus Torvalds 		if (iosapic_index < 0 ||
6621da177e4SLinus Torvalds 		    iosapic_lists[iosapic_index].node == MAX_NUMNODES)
6631da177e4SLinus Torvalds 			goto skip_numa_setup;
6641da177e4SLinus Torvalds 
6651da177e4SLinus Torvalds 		cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node);
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds 		for_each_cpu_mask(numa_cpu, cpu_mask) {
6681da177e4SLinus Torvalds 			if (!cpu_online(numa_cpu))
6691da177e4SLinus Torvalds 				cpu_clear(numa_cpu, cpu_mask);
6701da177e4SLinus Torvalds 		}
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 		num_cpus = cpus_weight(cpu_mask);
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 		if (!num_cpus)
6751da177e4SLinus Torvalds 			goto skip_numa_setup;
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 		/* Use vector assigment to distribute across cpus in node */
6781da177e4SLinus Torvalds 		cpu_index = vector % num_cpus;
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 		for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++)
6811da177e4SLinus Torvalds 			numa_cpu = next_cpu(numa_cpu, cpu_mask);
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 		if (numa_cpu != NR_CPUS)
6841da177e4SLinus Torvalds 			return cpu_physical_id(numa_cpu);
6851da177e4SLinus Torvalds 	}
6861da177e4SLinus Torvalds skip_numa_setup:
6871da177e4SLinus Torvalds #endif
6881da177e4SLinus Torvalds 	/*
6891da177e4SLinus Torvalds 	 * Otherwise, round-robin interrupt vectors across all the
6901da177e4SLinus Torvalds 	 * processors.  (It'd be nice if we could be smarter in the
6911da177e4SLinus Torvalds 	 * case of NUMA.)
6921da177e4SLinus Torvalds 	 */
6931da177e4SLinus Torvalds 	do {
6941da177e4SLinus Torvalds 		if (++cpu >= NR_CPUS)
6951da177e4SLinus Torvalds 			cpu = 0;
6961da177e4SLinus Torvalds 	} while (!cpu_online(cpu));
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	return cpu_physical_id(cpu);
6991da177e4SLinus Torvalds #else
7001da177e4SLinus Torvalds 	return cpu_physical_id(smp_processor_id());
7011da177e4SLinus Torvalds #endif
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds /*
7051da177e4SLinus Torvalds  * ACPI can describe IOSAPIC interrupts via static tables and namespace
7061da177e4SLinus Torvalds  * methods.  This provides an interface to register those interrupts and
7071da177e4SLinus Torvalds  * program the IOSAPIC RTE.
7081da177e4SLinus Torvalds  */
7091da177e4SLinus Torvalds int
7101da177e4SLinus Torvalds iosapic_register_intr (unsigned int gsi,
7111da177e4SLinus Torvalds 		       unsigned long polarity, unsigned long trigger)
7121da177e4SLinus Torvalds {
71324eeb568SKenji Kaneshige 	int vector, mask = 1;
7141da177e4SLinus Torvalds 	unsigned int dest;
7151da177e4SLinus Torvalds 	unsigned long flags;
71624eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
71724eeb568SKenji Kaneshige 	u32 low32;
71824eeb568SKenji Kaneshige again:
7191da177e4SLinus Torvalds 	/*
7201da177e4SLinus Torvalds 	 * If this GSI has already been registered (i.e., it's a
7211da177e4SLinus Torvalds 	 * shared interrupt, or we lost a race to register it),
7221da177e4SLinus Torvalds 	 * don't touch the RTE.
7231da177e4SLinus Torvalds 	 */
7241da177e4SLinus Torvalds 	spin_lock_irqsave(&iosapic_lock, flags);
7251da177e4SLinus Torvalds 	{
7261da177e4SLinus Torvalds 		vector = gsi_to_vector(gsi);
7271da177e4SLinus Torvalds 		if (vector > 0) {
72824eeb568SKenji Kaneshige 			rte = gsi_vector_to_rte(gsi, vector);
72924eeb568SKenji Kaneshige 			rte->refcnt++;
7301da177e4SLinus Torvalds 			spin_unlock_irqrestore(&iosapic_lock, flags);
7311da177e4SLinus Torvalds 			return vector;
7321da177e4SLinus Torvalds 		}
73324eeb568SKenji Kaneshige 	}
73424eeb568SKenji Kaneshige 	spin_unlock_irqrestore(&iosapic_lock, flags);
7351da177e4SLinus Torvalds 
73624eeb568SKenji Kaneshige 	/* If vector is running out, we try to find a sharable vector */
73724eeb568SKenji Kaneshige 	vector = assign_irq_vector_nopanic(AUTO_ASSIGN);
73824eeb568SKenji Kaneshige 	if (vector < 0)
73924eeb568SKenji Kaneshige 		vector = iosapic_find_sharable_vector(trigger, polarity);
74024eeb568SKenji Kaneshige 
74124eeb568SKenji Kaneshige 	spin_lock_irqsave(&irq_descp(vector)->lock, flags);
74224eeb568SKenji Kaneshige 	spin_lock(&iosapic_lock);
74324eeb568SKenji Kaneshige 	{
74424eeb568SKenji Kaneshige 		if (gsi_to_vector(gsi) > 0) {
74524eeb568SKenji Kaneshige 			if (list_empty(&iosapic_intr_info[vector].rtes))
74624eeb568SKenji Kaneshige 				free_irq_vector(vector);
74724eeb568SKenji Kaneshige 			spin_unlock(&iosapic_lock);
74824eeb568SKenji Kaneshige 			spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
74924eeb568SKenji Kaneshige 			goto again;
75024eeb568SKenji Kaneshige 		}
75124eeb568SKenji Kaneshige 
7521da177e4SLinus Torvalds 		dest = get_target_cpu(gsi, vector);
7531da177e4SLinus Torvalds 		register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
7541da177e4SLinus Torvalds 			      polarity, trigger);
7551da177e4SLinus Torvalds 
75624eeb568SKenji Kaneshige 		/*
75724eeb568SKenji Kaneshige 		 * If the vector is shared and already unmasked for
75824eeb568SKenji Kaneshige 		 * other interrupt sources, don't mask it.
75924eeb568SKenji Kaneshige 		 */
76024eeb568SKenji Kaneshige 		low32 = iosapic_intr_info[vector].low32;
76124eeb568SKenji Kaneshige 		if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK))
76224eeb568SKenji Kaneshige 			mask = 0;
76324eeb568SKenji Kaneshige 		set_rte(gsi, vector, dest, mask);
7641da177e4SLinus Torvalds 	}
765b9e41d7fSKenji Kaneshige 	spin_unlock(&iosapic_lock);
76624eeb568SKenji Kaneshige 	spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 	printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
7691da177e4SLinus Torvalds 	       gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
7701da177e4SLinus Torvalds 	       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
7711da177e4SLinus Torvalds 	       cpu_logical_id(dest), dest, vector);
7721da177e4SLinus Torvalds 
7731da177e4SLinus Torvalds 	return vector;
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds #ifdef CONFIG_ACPI_DEALLOCATE_IRQ
7771da177e4SLinus Torvalds void
7781da177e4SLinus Torvalds iosapic_unregister_intr (unsigned int gsi)
7791da177e4SLinus Torvalds {
7801da177e4SLinus Torvalds 	unsigned long flags;
7810e888adcSKenji Kaneshige 	int irq, vector, index;
7821da177e4SLinus Torvalds 	irq_desc_t *idesc;
78324eeb568SKenji Kaneshige 	u32 low32;
7841da177e4SLinus Torvalds 	unsigned long trigger, polarity;
78524eeb568SKenji Kaneshige 	unsigned int dest;
78624eeb568SKenji Kaneshige 	struct iosapic_rte_info *rte;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	/*
7891da177e4SLinus Torvalds 	 * If the irq associated with the gsi is not found,
7901da177e4SLinus Torvalds 	 * iosapic_unregister_intr() is unbalanced. We need to check
7911da177e4SLinus Torvalds 	 * this again after getting locks.
7921da177e4SLinus Torvalds 	 */
7931da177e4SLinus Torvalds 	irq = gsi_to_irq(gsi);
7941da177e4SLinus Torvalds 	if (irq < 0) {
7951da177e4SLinus Torvalds 		printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
7961da177e4SLinus Torvalds 		WARN_ON(1);
7971da177e4SLinus Torvalds 		return;
7981da177e4SLinus Torvalds 	}
7991da177e4SLinus Torvalds 	vector = irq_to_vector(irq);
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	idesc = irq_descp(irq);
8021da177e4SLinus Torvalds 	spin_lock_irqsave(&idesc->lock, flags);
8031da177e4SLinus Torvalds 	spin_lock(&iosapic_lock);
8041da177e4SLinus Torvalds 	{
80524eeb568SKenji Kaneshige 		if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) {
8061da177e4SLinus Torvalds 			printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
8071da177e4SLinus Torvalds 			WARN_ON(1);
80824eeb568SKenji Kaneshige 			goto out;
8091da177e4SLinus Torvalds 		}
8101da177e4SLinus Torvalds 
81124eeb568SKenji Kaneshige 		if (--rte->refcnt > 0)
81224eeb568SKenji Kaneshige 			goto out;
8131da177e4SLinus Torvalds 
81424eeb568SKenji Kaneshige 		/* Mask the interrupt */
81524eeb568SKenji Kaneshige 		low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK;
81624eeb568SKenji Kaneshige 		iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32);
8171da177e4SLinus Torvalds 
81824eeb568SKenji Kaneshige 		/* Remove the rte entry from the list */
81924eeb568SKenji Kaneshige 		list_del(&rte->rte_list);
82024eeb568SKenji Kaneshige 		iosapic_intr_info[vector].count--;
82124eeb568SKenji Kaneshige 		iosapic_free_rte(rte);
8220e888adcSKenji Kaneshige 		index = find_iosapic(gsi);
8230e888adcSKenji Kaneshige 		iosapic_lists[index].rtes_inuse--;
8240e888adcSKenji Kaneshige 		WARN_ON(iosapic_lists[index].rtes_inuse < 0);
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds 		trigger	 = iosapic_intr_info[vector].trigger;
8271da177e4SLinus Torvalds 		polarity = iosapic_intr_info[vector].polarity;
82824eeb568SKenji Kaneshige 		dest     = iosapic_intr_info[vector].dest;
82924eeb568SKenji Kaneshige 		printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n",
83024eeb568SKenji Kaneshige 		       gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
83124eeb568SKenji Kaneshige 		       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
83224eeb568SKenji Kaneshige 		       cpu_logical_id(dest), dest, vector);
8331da177e4SLinus Torvalds 
83424eeb568SKenji Kaneshige 		if (list_empty(&iosapic_intr_info[vector].rtes)) {
83524eeb568SKenji Kaneshige 			/* Sanity check */
83624eeb568SKenji Kaneshige 			BUG_ON(iosapic_intr_info[vector].count);
83724eeb568SKenji Kaneshige 
83824eeb568SKenji Kaneshige 			/* Clear the interrupt controller descriptor */
83924eeb568SKenji Kaneshige 			idesc->handler = &no_irq_type;
84024eeb568SKenji Kaneshige 
84124eeb568SKenji Kaneshige 			/* Clear the interrupt information */
8421da177e4SLinus Torvalds 			memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
84324eeb568SKenji Kaneshige 			iosapic_intr_info[vector].low32 |= IOSAPIC_MASK;
84424eeb568SKenji Kaneshige 			INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);
84524eeb568SKenji Kaneshige 
84624eeb568SKenji Kaneshige 			if (idesc->action) {
84724eeb568SKenji Kaneshige 				printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq);
84824eeb568SKenji Kaneshige 				WARN_ON(1);
8491da177e4SLinus Torvalds 			}
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 			/* Free the interrupt vector */
8521da177e4SLinus Torvalds 			free_irq_vector(vector);
85324eeb568SKenji Kaneshige 		}
85424eeb568SKenji Kaneshige 	}
85524eeb568SKenji Kaneshige  out:
85624eeb568SKenji Kaneshige 	spin_unlock(&iosapic_lock);
85724eeb568SKenji Kaneshige 	spin_unlock_irqrestore(&idesc->lock, flags);
8581da177e4SLinus Torvalds }
8591da177e4SLinus Torvalds #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds /*
8621da177e4SLinus Torvalds  * ACPI calls this when it finds an entry for a platform interrupt.
8631da177e4SLinus Torvalds  * Note that the irq_base and IOSAPIC address must be set in iosapic_init().
8641da177e4SLinus Torvalds  */
8651da177e4SLinus Torvalds int __init
8661da177e4SLinus Torvalds iosapic_register_platform_intr (u32 int_type, unsigned int gsi,
8671da177e4SLinus Torvalds 				int iosapic_vector, u16 eid, u16 id,
8681da177e4SLinus Torvalds 				unsigned long polarity, unsigned long trigger)
8691da177e4SLinus Torvalds {
8701da177e4SLinus Torvalds 	static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"};
8711da177e4SLinus Torvalds 	unsigned char delivery;
8721da177e4SLinus Torvalds 	int vector, mask = 0;
8731da177e4SLinus Torvalds 	unsigned int dest = ((id << 8) | eid) & 0xffff;
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	switch (int_type) {
8761da177e4SLinus Torvalds 	      case ACPI_INTERRUPT_PMI:
8771da177e4SLinus Torvalds 		vector = iosapic_vector;
8781da177e4SLinus Torvalds 		/*
8791da177e4SLinus Torvalds 		 * since PMI vector is alloc'd by FW(ACPI) not by kernel,
8801da177e4SLinus Torvalds 		 * we need to make sure the vector is available
8811da177e4SLinus Torvalds 		 */
8821da177e4SLinus Torvalds 		iosapic_reassign_vector(vector);
8831da177e4SLinus Torvalds 		delivery = IOSAPIC_PMI;
8841da177e4SLinus Torvalds 		break;
8851da177e4SLinus Torvalds 	      case ACPI_INTERRUPT_INIT:
8861da177e4SLinus Torvalds 		vector = assign_irq_vector(AUTO_ASSIGN);
8871da177e4SLinus Torvalds 		delivery = IOSAPIC_INIT;
8881da177e4SLinus Torvalds 		break;
8891da177e4SLinus Torvalds 	      case ACPI_INTERRUPT_CPEI:
8901da177e4SLinus Torvalds 		vector = IA64_CPE_VECTOR;
8911da177e4SLinus Torvalds 		delivery = IOSAPIC_LOWEST_PRIORITY;
8921da177e4SLinus Torvalds 		mask = 1;
8931da177e4SLinus Torvalds 		break;
8941da177e4SLinus Torvalds 	      default:
8951da177e4SLinus Torvalds 		printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type);
8961da177e4SLinus Torvalds 		return -1;
8971da177e4SLinus Torvalds 	}
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	register_intr(gsi, vector, delivery, polarity, trigger);
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
9021da177e4SLinus Torvalds 	       int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown",
9031da177e4SLinus Torvalds 	       int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
9041da177e4SLinus Torvalds 	       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
9051da177e4SLinus Torvalds 	       cpu_logical_id(dest), dest, vector);
9061da177e4SLinus Torvalds 
90724eeb568SKenji Kaneshige 	set_rte(gsi, vector, dest, mask);
9081da177e4SLinus Torvalds 	return vector;
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds /*
9131da177e4SLinus Torvalds  * ACPI calls this when it finds an entry for a legacy ISA IRQ override.
9141da177e4SLinus Torvalds  * Note that the gsi_base and IOSAPIC address must be set in iosapic_init().
9151da177e4SLinus Torvalds  */
9161da177e4SLinus Torvalds void __init
9171da177e4SLinus Torvalds iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
9181da177e4SLinus Torvalds 			  unsigned long polarity,
9191da177e4SLinus Torvalds 			  unsigned long trigger)
9201da177e4SLinus Torvalds {
9211da177e4SLinus Torvalds 	int vector;
9221da177e4SLinus Torvalds 	unsigned int dest = cpu_physical_id(smp_processor_id());
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	vector = isa_irq_to_vector(isa_irq);
9251da177e4SLinus Torvalds 
9261da177e4SLinus Torvalds 	register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n",
9291da177e4SLinus Torvalds 	    isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level",
9301da177e4SLinus Torvalds 	    polarity == IOSAPIC_POL_HIGH ? "high" : "low",
9311da177e4SLinus Torvalds 	    cpu_logical_id(dest), dest, vector);
9321da177e4SLinus Torvalds 
93324eeb568SKenji Kaneshige 	set_rte(gsi, vector, dest, 1);
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds void __init
9371da177e4SLinus Torvalds iosapic_system_init (int system_pcat_compat)
9381da177e4SLinus Torvalds {
9391da177e4SLinus Torvalds 	int vector;
9401da177e4SLinus Torvalds 
94124eeb568SKenji Kaneshige 	for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) {
94224eeb568SKenji Kaneshige 		iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
94324eeb568SKenji Kaneshige 		INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);	/* mark as unused */
94424eeb568SKenji Kaneshige 	}
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	pcat_compat = system_pcat_compat;
9471da177e4SLinus Torvalds 	if (pcat_compat) {
9481da177e4SLinus Torvalds 		/*
9491da177e4SLinus Torvalds 		 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
9501da177e4SLinus Torvalds 		 * enabled.
9511da177e4SLinus Torvalds 		 */
9521da177e4SLinus Torvalds 		printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
9531da177e4SLinus Torvalds 		outb(0xff, 0xA1);
9541da177e4SLinus Torvalds 		outb(0xff, 0x21);
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds }
9571da177e4SLinus Torvalds 
9580e888adcSKenji Kaneshige static inline int
9590e888adcSKenji Kaneshige iosapic_alloc (void)
9600e888adcSKenji Kaneshige {
9610e888adcSKenji Kaneshige 	int index;
9620e888adcSKenji Kaneshige 
9630e888adcSKenji Kaneshige 	for (index = 0; index < NR_IOSAPICS; index++)
9640e888adcSKenji Kaneshige 		if (!iosapic_lists[index].addr)
9650e888adcSKenji Kaneshige 			return index;
9660e888adcSKenji Kaneshige 
9670e888adcSKenji Kaneshige 	printk(KERN_WARNING "%s: failed to allocate iosapic\n", __FUNCTION__);
9680e888adcSKenji Kaneshige 	return -1;
9690e888adcSKenji Kaneshige }
9700e888adcSKenji Kaneshige 
9710e888adcSKenji Kaneshige static inline void
9720e888adcSKenji Kaneshige iosapic_free (int index)
9730e888adcSKenji Kaneshige {
9740e888adcSKenji Kaneshige 	memset(&iosapic_lists[index], 0, sizeof(iosapic_lists[0]));
9750e888adcSKenji Kaneshige }
9760e888adcSKenji Kaneshige 
9770e888adcSKenji Kaneshige static inline int
9780e888adcSKenji Kaneshige iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver)
9790e888adcSKenji Kaneshige {
9800e888adcSKenji Kaneshige 	int index;
9810e888adcSKenji Kaneshige 	unsigned int gsi_end, base, end;
9820e888adcSKenji Kaneshige 
9830e888adcSKenji Kaneshige 	/* check gsi range */
9840e888adcSKenji Kaneshige 	gsi_end = gsi_base + ((ver >> 16) & 0xff);
9850e888adcSKenji Kaneshige 	for (index = 0; index < NR_IOSAPICS; index++) {
9860e888adcSKenji Kaneshige 		if (!iosapic_lists[index].addr)
9870e888adcSKenji Kaneshige 			continue;
9880e888adcSKenji Kaneshige 
9890e888adcSKenji Kaneshige 		base = iosapic_lists[index].gsi_base;
9900e888adcSKenji Kaneshige 		end  = base + iosapic_lists[index].num_rte - 1;
9910e888adcSKenji Kaneshige 
9920e888adcSKenji Kaneshige 		if (gsi_base < base && gsi_end < base)
9930e888adcSKenji Kaneshige 			continue;/* OK */
9940e888adcSKenji Kaneshige 
9950e888adcSKenji Kaneshige 		if (gsi_base > end && gsi_end > end)
9960e888adcSKenji Kaneshige 			continue; /* OK */
9970e888adcSKenji Kaneshige 
9980e888adcSKenji Kaneshige 		return -EBUSY;
9990e888adcSKenji Kaneshige 	}
10000e888adcSKenji Kaneshige 	return 0;
10010e888adcSKenji Kaneshige }
10020e888adcSKenji Kaneshige 
10030e888adcSKenji Kaneshige int __devinit
10041da177e4SLinus Torvalds iosapic_init (unsigned long phys_addr, unsigned int gsi_base)
10051da177e4SLinus Torvalds {
10060e888adcSKenji Kaneshige 	int num_rte, err, index;
10071da177e4SLinus Torvalds 	unsigned int isa_irq, ver;
10081da177e4SLinus Torvalds 	char __iomem *addr;
10090e888adcSKenji Kaneshige 	unsigned long flags;
10101da177e4SLinus Torvalds 
10110e888adcSKenji Kaneshige 	spin_lock_irqsave(&iosapic_lock, flags);
10120e888adcSKenji Kaneshige 	{
10131da177e4SLinus Torvalds 		addr = ioremap(phys_addr, 0);
10141da177e4SLinus Torvalds 		ver = iosapic_version(addr);
10151da177e4SLinus Torvalds 
10160e888adcSKenji Kaneshige 		if ((err = iosapic_check_gsi_range(gsi_base, ver))) {
10170e888adcSKenji Kaneshige 			iounmap(addr);
10180e888adcSKenji Kaneshige 			spin_unlock_irqrestore(&iosapic_lock, flags);
10190e888adcSKenji Kaneshige 			return err;
10200e888adcSKenji Kaneshige 		}
10210e888adcSKenji Kaneshige 
10221da177e4SLinus Torvalds 		/*
10231da177e4SLinus Torvalds 		 * The MAX_REDIR register holds the highest input pin
10241da177e4SLinus Torvalds 		 * number (starting from 0).
10251da177e4SLinus Torvalds 		 * We add 1 so that we can use it for number of pins (= RTEs)
10261da177e4SLinus Torvalds 		 */
10271da177e4SLinus Torvalds 		num_rte = ((ver >> 16) & 0xff) + 1;
10281da177e4SLinus Torvalds 
10290e888adcSKenji Kaneshige 		index = iosapic_alloc();
10300e888adcSKenji Kaneshige 		iosapic_lists[index].addr = addr;
10310e888adcSKenji Kaneshige 		iosapic_lists[index].gsi_base = gsi_base;
10320e888adcSKenji Kaneshige 		iosapic_lists[index].num_rte = num_rte;
10331da177e4SLinus Torvalds #ifdef CONFIG_NUMA
10340e888adcSKenji Kaneshige 		iosapic_lists[index].node = MAX_NUMNODES;
10351da177e4SLinus Torvalds #endif
10360e888adcSKenji Kaneshige 	}
10370e888adcSKenji Kaneshige 	spin_unlock_irqrestore(&iosapic_lock, flags);
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 	if ((gsi_base == 0) && pcat_compat) {
10401da177e4SLinus Torvalds 		/*
10411da177e4SLinus Torvalds 		 * Map the legacy ISA devices into the IOSAPIC data.  Some of these may
10421da177e4SLinus Torvalds 		 * get reprogrammed later on with data from the ACPI Interrupt Source
10431da177e4SLinus Torvalds 		 * Override table.
10441da177e4SLinus Torvalds 		 */
10451da177e4SLinus Torvalds 		for (isa_irq = 0; isa_irq < 16; ++isa_irq)
10461da177e4SLinus Torvalds 			iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE);
10471da177e4SLinus Torvalds 	}
10480e888adcSKenji Kaneshige 	return 0;
10491da177e4SLinus Torvalds }
10501da177e4SLinus Torvalds 
10510e888adcSKenji Kaneshige #ifdef CONFIG_HOTPLUG
10520e888adcSKenji Kaneshige int
10530e888adcSKenji Kaneshige iosapic_remove (unsigned int gsi_base)
10540e888adcSKenji Kaneshige {
10550e888adcSKenji Kaneshige 	int index, err = 0;
10560e888adcSKenji Kaneshige 	unsigned long flags;
10570e888adcSKenji Kaneshige 
10580e888adcSKenji Kaneshige 	spin_lock_irqsave(&iosapic_lock, flags);
10590e888adcSKenji Kaneshige 	{
10600e888adcSKenji Kaneshige 		index = find_iosapic(gsi_base);
10610e888adcSKenji Kaneshige 		if (index < 0) {
10620e888adcSKenji Kaneshige 			printk(KERN_WARNING "%s: No IOSAPIC for GSI base %u\n",
10630e888adcSKenji Kaneshige 			       __FUNCTION__, gsi_base);
10640e888adcSKenji Kaneshige 			goto out;
10650e888adcSKenji Kaneshige 		}
10660e888adcSKenji Kaneshige 
10670e888adcSKenji Kaneshige 		if (iosapic_lists[index].rtes_inuse) {
10680e888adcSKenji Kaneshige 			err = -EBUSY;
10690e888adcSKenji Kaneshige 			printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n",
10700e888adcSKenji Kaneshige 			       __FUNCTION__, gsi_base);
10710e888adcSKenji Kaneshige 			goto out;
10720e888adcSKenji Kaneshige 		}
10730e888adcSKenji Kaneshige 
10740e888adcSKenji Kaneshige 		iounmap(iosapic_lists[index].addr);
10750e888adcSKenji Kaneshige 		iosapic_free(index);
10760e888adcSKenji Kaneshige 	}
10770e888adcSKenji Kaneshige  out:
10780e888adcSKenji Kaneshige 	spin_unlock_irqrestore(&iosapic_lock, flags);
10790e888adcSKenji Kaneshige 	return err;
10800e888adcSKenji Kaneshige }
10810e888adcSKenji Kaneshige #endif /* CONFIG_HOTPLUG */
10820e888adcSKenji Kaneshige 
10831da177e4SLinus Torvalds #ifdef CONFIG_NUMA
10840e888adcSKenji Kaneshige void __devinit
10851da177e4SLinus Torvalds map_iosapic_to_node(unsigned int gsi_base, int node)
10861da177e4SLinus Torvalds {
10871da177e4SLinus Torvalds 	int index;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	index = find_iosapic(gsi_base);
10901da177e4SLinus Torvalds 	if (index < 0) {
10911da177e4SLinus Torvalds 		printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n",
10921da177e4SLinus Torvalds 		       __FUNCTION__, gsi_base);
10931da177e4SLinus Torvalds 		return;
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds 	iosapic_lists[index].node = node;
10961da177e4SLinus Torvalds 	return;
10971da177e4SLinus Torvalds }
10981da177e4SLinus Torvalds #endif
109924eeb568SKenji Kaneshige 
110024eeb568SKenji Kaneshige static int __init iosapic_enable_kmalloc (void)
110124eeb568SKenji Kaneshige {
110224eeb568SKenji Kaneshige 	iosapic_kmalloc_ok = 1;
110324eeb568SKenji Kaneshige 	return 0;
110424eeb568SKenji Kaneshige }
110524eeb568SKenji Kaneshige core_initcall (iosapic_enable_kmalloc);
1106