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 * 1246cba3dcSSatoru Takeuchi * 00/04/19 D. Mosberger Rewritten to mirror more closely the x86 I/O 1346cba3dcSSatoru Takeuchi * APIC code. In particular, we now have separate 1446cba3dcSSatoru Takeuchi * handlers for edge and level triggered 1546cba3dcSSatoru Takeuchi * interrupts. 1646cba3dcSSatoru Takeuchi * 00/10/27 Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector 1746cba3dcSSatoru Takeuchi * allocation PCI to vector mapping, shared PCI 1846cba3dcSSatoru Takeuchi * interrupts. 1946cba3dcSSatoru Takeuchi * 00/10/27 D. Mosberger Document things a bit more to make them more 2046cba3dcSSatoru Takeuchi * understandable. Clean up much of the old 2146cba3dcSSatoru Takeuchi * IOSAPIC cruft. 2246cba3dcSSatoru Takeuchi * 01/07/27 J.I. Lee PCI irq routing, Platform/Legacy interrupts 2346cba3dcSSatoru Takeuchi * and fixes for ACPI S5(SoftOff) support. 241da177e4SLinus Torvalds * 02/01/23 J.I. Lee iosapic pgm fixes for PCI irq routing from _PRT 2546cba3dcSSatoru Takeuchi * 02/01/07 E. Focht <efocht@ess.nec.de> Redirectable interrupt 2646cba3dcSSatoru Takeuchi * vectors in iosapic_set_affinity(), 2746cba3dcSSatoru Takeuchi * initializations for /proc/irq/#/smp_affinity 281da177e4SLinus Torvalds * 02/04/02 P. Diefenbaugh Cleaned up ACPI PCI IRQ routing. 291da177e4SLinus Torvalds * 02/04/18 J.I. Lee bug fix in iosapic_init_pci_irq 3046cba3dcSSatoru Takeuchi * 02/04/30 J.I. Lee bug fix in find_iosapic to fix ACPI PCI IRQ to 3146cba3dcSSatoru Takeuchi * IOSAPIC mapping error 321da177e4SLinus Torvalds * 02/07/29 T. Kochi Allocate interrupt vectors dynamically 3346cba3dcSSatoru Takeuchi * 02/08/04 T. Kochi Cleaned up terminology (irq, global system 3446cba3dcSSatoru Takeuchi * interrupt, vector, etc.) 3546cba3dcSSatoru Takeuchi * 02/09/20 D. Mosberger Simplified by taking advantage of ACPI's 3646cba3dcSSatoru Takeuchi * pci_irq code. 371da177e4SLinus Torvalds * 03/02/19 B. Helgaas Make pcat_compat system-wide, not per-IOSAPIC. 3846cba3dcSSatoru Takeuchi * Remove iosapic_address & gsi_base from 3946cba3dcSSatoru Takeuchi * external interfaces. Rationalize 4046cba3dcSSatoru Takeuchi * __init/__devinit attributes. 411da177e4SLinus Torvalds * 04/12/04 Ashok Raj <ashok.raj@intel.com> Intel Corporation 2004 4246cba3dcSSatoru Takeuchi * Updated to work with irq migration necessary 4346cba3dcSSatoru Takeuchi * for CPU Hotplug 441da177e4SLinus Torvalds */ 451da177e4SLinus Torvalds /* 4646cba3dcSSatoru Takeuchi * Here is what the interrupt logic between a PCI device and the kernel looks 4746cba3dcSSatoru Takeuchi * like: 481da177e4SLinus Torvalds * 4946cba3dcSSatoru Takeuchi * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, 5046cba3dcSSatoru Takeuchi * INTD). The device is uniquely identified by its bus-, and slot-number 5146cba3dcSSatoru Takeuchi * (the function number does not matter here because all functions share 5246cba3dcSSatoru Takeuchi * the same interrupt lines). 531da177e4SLinus Torvalds * 5446cba3dcSSatoru Takeuchi * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC 5546cba3dcSSatoru Takeuchi * controller. Multiple interrupt lines may have to share the same 5646cba3dcSSatoru Takeuchi * IOSAPIC pin (if they're level triggered and use the same polarity). 5746cba3dcSSatoru Takeuchi * Each interrupt line has a unique Global System Interrupt (GSI) number 5846cba3dcSSatoru Takeuchi * which can be calculated as the sum of the controller's base GSI number 5946cba3dcSSatoru Takeuchi * and the IOSAPIC pin number to which the line connects. 601da177e4SLinus Torvalds * 6146cba3dcSSatoru Takeuchi * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the 6246cba3dcSSatoru Takeuchi * IOSAPIC pin into the IA-64 interrupt vector. This interrupt vector is then 6346cba3dcSSatoru Takeuchi * sent to the CPU. 641da177e4SLinus Torvalds * 6546cba3dcSSatoru Takeuchi * (4) The kernel recognizes an interrupt as an IRQ. The IRQ interface is 6646cba3dcSSatoru Takeuchi * used as architecture-independent interrupt handling mechanism in Linux. 6746cba3dcSSatoru Takeuchi * As an IRQ is a number, we have to have 6846cba3dcSSatoru Takeuchi * IA-64 interrupt vector number <-> IRQ number mapping. On smaller 6946cba3dcSSatoru Takeuchi * systems, we use one-to-one mapping between IA-64 vector and IRQ. A 7046cba3dcSSatoru Takeuchi * platform can implement platform_irq_to_vector(irq) and 711da177e4SLinus Torvalds * platform_local_vector_to_irq(vector) APIs to differentiate the mapping. 721da177e4SLinus Torvalds * Please see also include/asm-ia64/hw_irq.h for those APIs. 731da177e4SLinus Torvalds * 741da177e4SLinus Torvalds * To sum up, there are three levels of mappings involved: 751da177e4SLinus Torvalds * 761da177e4SLinus Torvalds * PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ 771da177e4SLinus Torvalds * 7846cba3dcSSatoru Takeuchi * Note: The term "IRQ" is loosely used everywhere in Linux kernel to 7946cba3dcSSatoru Takeuchi * describeinterrupts. Now we use "IRQ" only for Linux IRQ's. ISA IRQ 8046cba3dcSSatoru Takeuchi * (isa_irq) is the only exception in this source code. 811da177e4SLinus Torvalds */ 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds #include <linux/acpi.h> 841da177e4SLinus Torvalds #include <linux/init.h> 851da177e4SLinus Torvalds #include <linux/irq.h> 861da177e4SLinus Torvalds #include <linux/kernel.h> 871da177e4SLinus Torvalds #include <linux/list.h> 881da177e4SLinus Torvalds #include <linux/pci.h> 891da177e4SLinus Torvalds #include <linux/smp.h> 901da177e4SLinus Torvalds #include <linux/string.h> 9124eeb568SKenji Kaneshige #include <linux/bootmem.h> 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds #include <asm/delay.h> 941da177e4SLinus Torvalds #include <asm/hw_irq.h> 951da177e4SLinus Torvalds #include <asm/io.h> 961da177e4SLinus Torvalds #include <asm/iosapic.h> 971da177e4SLinus Torvalds #include <asm/machvec.h> 981da177e4SLinus Torvalds #include <asm/processor.h> 991da177e4SLinus Torvalds #include <asm/ptrace.h> 1001da177e4SLinus Torvalds #include <asm/system.h> 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds #undef DEBUG_INTERRUPT_ROUTING 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds #ifdef DEBUG_INTERRUPT_ROUTING 1051da177e4SLinus Torvalds #define DBG(fmt...) printk(fmt) 1061da177e4SLinus Torvalds #else 1071da177e4SLinus Torvalds #define DBG(fmt...) 1081da177e4SLinus Torvalds #endif 1091da177e4SLinus Torvalds 11046cba3dcSSatoru Takeuchi #define NR_PREALLOCATE_RTE_ENTRIES \ 11146cba3dcSSatoru Takeuchi (PAGE_SIZE / sizeof(struct iosapic_rte_info)) 11224eeb568SKenji Kaneshige #define RTE_PREALLOCATED (1) 11324eeb568SKenji Kaneshige 1141da177e4SLinus Torvalds static DEFINE_SPINLOCK(iosapic_lock); 1151da177e4SLinus Torvalds 11646cba3dcSSatoru Takeuchi /* 11746cba3dcSSatoru Takeuchi * These tables map IA-64 vectors to the IOSAPIC pin that generates this 11846cba3dcSSatoru Takeuchi * vector. 11946cba3dcSSatoru Takeuchi */ 120e1b30a39SYasuaki Ishimatsu 121e1b30a39SYasuaki Ishimatsu #define NO_REF_RTE 0 122e1b30a39SYasuaki Ishimatsu 123c5e3f9e5SYasuaki Ishimatsu static struct iosapic { 124c5e3f9e5SYasuaki Ishimatsu char __iomem *addr; /* base address of IOSAPIC */ 125c5e3f9e5SYasuaki Ishimatsu unsigned int gsi_base; /* GSI base */ 126c5e3f9e5SYasuaki Ishimatsu unsigned short num_rte; /* # of RTEs on this IOSAPIC */ 127c5e3f9e5SYasuaki Ishimatsu int rtes_inuse; /* # of RTEs in use on this IOSAPIC */ 128c5e3f9e5SYasuaki Ishimatsu #ifdef CONFIG_NUMA 129c5e3f9e5SYasuaki Ishimatsu unsigned short node; /* numa node association via pxm */ 130c5e3f9e5SYasuaki Ishimatsu #endif 131c1726d6fSYasuaki Ishimatsu spinlock_t lock; /* lock for indirect reg access */ 132c5e3f9e5SYasuaki Ishimatsu } iosapic_lists[NR_IOSAPICS]; 1331da177e4SLinus Torvalds 13424eeb568SKenji Kaneshige struct iosapic_rte_info { 135c5e3f9e5SYasuaki Ishimatsu struct list_head rte_list; /* RTEs sharing the same vector */ 13624eeb568SKenji Kaneshige char rte_index; /* IOSAPIC RTE index */ 13724eeb568SKenji Kaneshige int refcnt; /* reference counter */ 13824eeb568SKenji Kaneshige unsigned int flags; /* flags */ 139c5e3f9e5SYasuaki Ishimatsu struct iosapic *iosapic; 14024eeb568SKenji Kaneshige } ____cacheline_aligned; 14124eeb568SKenji Kaneshige 14224eeb568SKenji Kaneshige static struct iosapic_intr_info { 14346cba3dcSSatoru Takeuchi struct list_head rtes; /* RTEs using this vector (empty => 14446cba3dcSSatoru Takeuchi * not an IOSAPIC interrupt) */ 145c4c376f7SKenji Kaneshige int count; /* # of registered RTEs */ 14646cba3dcSSatoru Takeuchi u32 low32; /* current value of low word of 14746cba3dcSSatoru Takeuchi * Redirection table entry */ 14824eeb568SKenji Kaneshige unsigned int dest; /* destination CPU physical ID */ 1491da177e4SLinus Torvalds unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ 15046cba3dcSSatoru Takeuchi unsigned char polarity: 1; /* interrupt polarity 15146cba3dcSSatoru Takeuchi * (see iosapic.h) */ 1521da177e4SLinus Torvalds unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ 1534bbdec7aSYasuaki Ishimatsu } iosapic_intr_info[NR_IRQS]; 1541da177e4SLinus Torvalds 1550e888adcSKenji Kaneshige static unsigned char pcat_compat __devinitdata; /* 8259 compatibility flag */ 1561da177e4SLinus Torvalds 15724eeb568SKenji Kaneshige static int iosapic_kmalloc_ok; 15824eeb568SKenji Kaneshige static LIST_HEAD(free_rte_list); 1591da177e4SLinus Torvalds 160c1726d6fSYasuaki Ishimatsu static inline void 161c1726d6fSYasuaki Ishimatsu iosapic_write(struct iosapic *iosapic, unsigned int reg, u32 val) 162c1726d6fSYasuaki Ishimatsu { 163c1726d6fSYasuaki Ishimatsu unsigned long flags; 164c1726d6fSYasuaki Ishimatsu 165c1726d6fSYasuaki Ishimatsu spin_lock_irqsave(&iosapic->lock, flags); 166c1726d6fSYasuaki Ishimatsu __iosapic_write(iosapic->addr, reg, val); 167c1726d6fSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic->lock, flags); 168c1726d6fSYasuaki Ishimatsu } 169c1726d6fSYasuaki Ishimatsu 1701da177e4SLinus Torvalds /* 1711da177e4SLinus Torvalds * Find an IOSAPIC associated with a GSI 1721da177e4SLinus Torvalds */ 1731da177e4SLinus Torvalds static inline int 1741da177e4SLinus Torvalds find_iosapic (unsigned int gsi) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds int i; 1771da177e4SLinus Torvalds 1780e888adcSKenji Kaneshige for (i = 0; i < NR_IOSAPICS; i++) { 17946cba3dcSSatoru Takeuchi if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < 18046cba3dcSSatoru Takeuchi iosapic_lists[i].num_rte) 1811da177e4SLinus Torvalds return i; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds return -1; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1874bbdec7aSYasuaki Ishimatsu static inline int __gsi_to_irq(unsigned int gsi) 1881da177e4SLinus Torvalds { 1894bbdec7aSYasuaki Ishimatsu int irq; 1901da177e4SLinus Torvalds struct iosapic_intr_info *info; 19124eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 1921da177e4SLinus Torvalds 1934bbdec7aSYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; irq++) { 1944bbdec7aSYasuaki Ishimatsu info = &iosapic_intr_info[irq]; 19524eeb568SKenji Kaneshige list_for_each_entry(rte, &info->rtes, rte_list) 196c5e3f9e5SYasuaki Ishimatsu if (rte->iosapic->gsi_base + rte->rte_index == gsi) 1974bbdec7aSYasuaki Ishimatsu return irq; 1984bbdec7aSYasuaki Ishimatsu } 1991da177e4SLinus Torvalds return -1; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds /* 2031da177e4SLinus Torvalds * Translate GSI number to the corresponding IA-64 interrupt vector. If no 2041da177e4SLinus Torvalds * entry exists, return -1. 2051da177e4SLinus Torvalds */ 2061da177e4SLinus Torvalds inline int 2071da177e4SLinus Torvalds gsi_to_vector (unsigned int gsi) 2081da177e4SLinus Torvalds { 2094bbdec7aSYasuaki Ishimatsu int irq = __gsi_to_irq(gsi); 210e1b30a39SYasuaki Ishimatsu if (check_irq_used(irq) < 0) 2114bbdec7aSYasuaki Ishimatsu return -1; 2124bbdec7aSYasuaki Ishimatsu return irq_to_vector(irq); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds int 2161da177e4SLinus Torvalds gsi_to_irq (unsigned int gsi) 2171da177e4SLinus Torvalds { 21824eeb568SKenji Kaneshige unsigned long flags; 21924eeb568SKenji Kaneshige int irq; 22024eeb568SKenji Kaneshige 2214bbdec7aSYasuaki Ishimatsu spin_lock_irqsave(&iosapic_lock, flags); 2224bbdec7aSYasuaki Ishimatsu irq = __gsi_to_irq(gsi); 2234bbdec7aSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 22424eeb568SKenji Kaneshige return irq; 22524eeb568SKenji Kaneshige } 22624eeb568SKenji Kaneshige 2274bbdec7aSYasuaki Ishimatsu static struct iosapic_rte_info *find_rte(unsigned int irq, unsigned int gsi) 22824eeb568SKenji Kaneshige { 22924eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 23024eeb568SKenji Kaneshige 2314bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) 232c5e3f9e5SYasuaki Ishimatsu if (rte->iosapic->gsi_base + rte->rte_index == gsi) 23324eeb568SKenji Kaneshige return rte; 23424eeb568SKenji Kaneshige return NULL; 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds static void 2384bbdec7aSYasuaki Ishimatsu set_rte (unsigned int gsi, unsigned int irq, unsigned int dest, int mask) 2391da177e4SLinus Torvalds { 2401da177e4SLinus Torvalds unsigned long pol, trigger, dmode; 2411da177e4SLinus Torvalds u32 low32, high32; 2421da177e4SLinus Torvalds int rte_index; 2431da177e4SLinus Torvalds char redir; 24424eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 2454bbdec7aSYasuaki Ishimatsu ia64_vector vector = irq_to_vector(irq); 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); 2481da177e4SLinus Torvalds 2494bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 25024eeb568SKenji Kaneshige if (!rte) 2511da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 2521da177e4SLinus Torvalds 25324eeb568SKenji Kaneshige rte_index = rte->rte_index; 2544bbdec7aSYasuaki Ishimatsu pol = iosapic_intr_info[irq].polarity; 2554bbdec7aSYasuaki Ishimatsu trigger = iosapic_intr_info[irq].trigger; 2564bbdec7aSYasuaki Ishimatsu dmode = iosapic_intr_info[irq].dmode; 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0; 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds #ifdef CONFIG_SMP 2614bbdec7aSYasuaki Ishimatsu set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); 2621da177e4SLinus Torvalds #endif 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds low32 = ((pol << IOSAPIC_POLARITY_SHIFT) | 2651da177e4SLinus Torvalds (trigger << IOSAPIC_TRIGGER_SHIFT) | 2661da177e4SLinus Torvalds (dmode << IOSAPIC_DELIVERY_SHIFT) | 2671da177e4SLinus Torvalds ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) | 2681da177e4SLinus Torvalds vector); 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds /* dest contains both id and eid */ 2711da177e4SLinus Torvalds high32 = (dest << IOSAPIC_DEST_SHIFT); 2721da177e4SLinus Torvalds 273c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_HIGH(rte_index), high32); 274c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 2754bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = low32; 2764bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dest = dest; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static void 28046cba3dcSSatoru Takeuchi nop (unsigned int irq) 2811da177e4SLinus Torvalds { 2821da177e4SLinus Torvalds /* do nothing... */ 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 285a7956113SZou Nan hai 286a7956113SZou Nan hai #ifdef CONFIG_KEXEC 287a7956113SZou Nan hai void 288a7956113SZou Nan hai kexec_disable_iosapic(void) 289a7956113SZou Nan hai { 290a7956113SZou Nan hai struct iosapic_intr_info *info; 291a7956113SZou Nan hai struct iosapic_rte_info *rte; 2924bbdec7aSYasuaki Ishimatsu ia64_vector vec; 2934bbdec7aSYasuaki Ishimatsu int irq; 2944bbdec7aSYasuaki Ishimatsu 2954bbdec7aSYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; irq++) { 2964bbdec7aSYasuaki Ishimatsu info = &iosapic_intr_info[irq]; 2974bbdec7aSYasuaki Ishimatsu vec = irq_to_vector(irq); 298a7956113SZou Nan hai list_for_each_entry(rte, &info->rtes, 299a7956113SZou Nan hai rte_list) { 300c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, 301a7956113SZou Nan hai IOSAPIC_RTE_LOW(rte->rte_index), 302a7956113SZou Nan hai IOSAPIC_MASK|vec); 303c5e3f9e5SYasuaki Ishimatsu iosapic_eoi(rte->iosapic->addr, vec); 304a7956113SZou Nan hai } 305a7956113SZou Nan hai } 306a7956113SZou Nan hai } 307a7956113SZou Nan hai #endif 308a7956113SZou Nan hai 3091da177e4SLinus Torvalds static void 3101da177e4SLinus Torvalds mask_irq (unsigned int irq) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds u32 low32; 3131da177e4SLinus Torvalds int rte_index; 31424eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3151da177e4SLinus Torvalds 316c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3171da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds /* set only the mask bit */ 3204bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 |= IOSAPIC_MASK; 3214bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 32224eeb568SKenji Kaneshige rte_index = rte->rte_index; 323c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds static void 3281da177e4SLinus Torvalds unmask_irq (unsigned int irq) 3291da177e4SLinus Torvalds { 3301da177e4SLinus Torvalds u32 low32; 3311da177e4SLinus Torvalds int rte_index; 33224eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3331da177e4SLinus Torvalds 334c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3351da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 3361da177e4SLinus Torvalds 3374bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 &= ~IOSAPIC_MASK; 3384bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 33924eeb568SKenji Kaneshige rte_index = rte->rte_index; 340c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds static void 3461da177e4SLinus Torvalds iosapic_set_affinity (unsigned int irq, cpumask_t mask) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds #ifdef CONFIG_SMP 3491da177e4SLinus Torvalds u32 high32, low32; 3501da177e4SLinus Torvalds int dest, rte_index; 3511da177e4SLinus Torvalds int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; 35224eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 353c1726d6fSYasuaki Ishimatsu struct iosapic *iosapic; 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds irq &= (~IA64_IRQ_REDIRECTED); 3561da177e4SLinus Torvalds 357cd378f18SYasuaki Ishimatsu cpus_and(mask, mask, cpu_online_map); 3581da177e4SLinus Torvalds if (cpus_empty(mask)) 3591da177e4SLinus Torvalds return; 3601da177e4SLinus Torvalds 361cd378f18SYasuaki Ishimatsu if (reassign_irq_vector(irq, first_cpu(mask))) 362cd378f18SYasuaki Ishimatsu return; 363cd378f18SYasuaki Ishimatsu 3641da177e4SLinus Torvalds dest = cpu_physical_id(first_cpu(mask)); 3651da177e4SLinus Torvalds 366c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3671da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds set_irq_affinity_info(irq, dest, redir); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds /* dest contains both id and eid */ 3721da177e4SLinus Torvalds high32 = dest << IOSAPIC_DEST_SHIFT; 3731da177e4SLinus Torvalds 3744bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); 3751da177e4SLinus Torvalds if (redir) 3761da177e4SLinus Torvalds /* change delivery mode to lowest priority */ 377e3a8f7b8SYasuaki Ishimatsu low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); 3781da177e4SLinus Torvalds else 3791da177e4SLinus Torvalds /* change delivery mode to fixed */ 3801da177e4SLinus Torvalds low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); 381cd378f18SYasuaki Ishimatsu low32 &= IOSAPIC_VECTOR_MASK; 382cd378f18SYasuaki Ishimatsu low32 |= irq_to_vector(irq); 3831da177e4SLinus Torvalds 3844bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = low32; 3854bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dest = dest; 3864bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 387c1726d6fSYasuaki Ishimatsu iosapic = rte->iosapic; 38824eeb568SKenji Kaneshige rte_index = rte->rte_index; 389c1726d6fSYasuaki Ishimatsu iosapic_write(iosapic, IOSAPIC_RTE_HIGH(rte_index), high32); 390c1726d6fSYasuaki Ishimatsu iosapic_write(iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds #endif 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* 3961da177e4SLinus Torvalds * Handlers for level-triggered interrupts. 3971da177e4SLinus Torvalds */ 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds static unsigned int 4001da177e4SLinus Torvalds iosapic_startup_level_irq (unsigned int irq) 4011da177e4SLinus Torvalds { 4021da177e4SLinus Torvalds unmask_irq(irq); 4031da177e4SLinus Torvalds return 0; 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds static void 4071da177e4SLinus Torvalds iosapic_end_level_irq (unsigned int irq) 4081da177e4SLinus Torvalds { 4091da177e4SLinus Torvalds ia64_vector vec = irq_to_vector(irq); 41024eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 411cd378f18SYasuaki Ishimatsu int do_unmask_irq = 0; 4121da177e4SLinus Torvalds 413cd378f18SYasuaki Ishimatsu if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) { 414cd378f18SYasuaki Ishimatsu do_unmask_irq = 1; 415cd378f18SYasuaki Ishimatsu mask_irq(irq); 416cd378f18SYasuaki Ishimatsu } 417cd378f18SYasuaki Ishimatsu 4184bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) 419c5e3f9e5SYasuaki Ishimatsu iosapic_eoi(rte->iosapic->addr, vec); 420cd378f18SYasuaki Ishimatsu 421cd378f18SYasuaki Ishimatsu if (unlikely(do_unmask_irq)) { 422cd378f18SYasuaki Ishimatsu move_masked_irq(irq); 423cd378f18SYasuaki Ishimatsu unmask_irq(irq); 424cd378f18SYasuaki Ishimatsu } 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds #define iosapic_shutdown_level_irq mask_irq 4281da177e4SLinus Torvalds #define iosapic_enable_level_irq unmask_irq 4291da177e4SLinus Torvalds #define iosapic_disable_level_irq mask_irq 4301da177e4SLinus Torvalds #define iosapic_ack_level_irq nop 4311da177e4SLinus Torvalds 432e253eb0cSKAMEZAWA Hiroyuki struct irq_chip irq_type_iosapic_level = { 43306344db3SIngo Molnar .name = "IO-SAPIC-level", 4341da177e4SLinus Torvalds .startup = iosapic_startup_level_irq, 4351da177e4SLinus Torvalds .shutdown = iosapic_shutdown_level_irq, 4361da177e4SLinus Torvalds .enable = iosapic_enable_level_irq, 4371da177e4SLinus Torvalds .disable = iosapic_disable_level_irq, 4381da177e4SLinus Torvalds .ack = iosapic_ack_level_irq, 4391da177e4SLinus Torvalds .end = iosapic_end_level_irq, 440e253eb0cSKAMEZAWA Hiroyuki .mask = mask_irq, 441e253eb0cSKAMEZAWA Hiroyuki .unmask = unmask_irq, 4421da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4431da177e4SLinus Torvalds }; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds /* 4461da177e4SLinus Torvalds * Handlers for edge-triggered interrupts. 4471da177e4SLinus Torvalds */ 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds static unsigned int 4501da177e4SLinus Torvalds iosapic_startup_edge_irq (unsigned int irq) 4511da177e4SLinus Torvalds { 4521da177e4SLinus Torvalds unmask_irq(irq); 4531da177e4SLinus Torvalds /* 4541da177e4SLinus Torvalds * IOSAPIC simply drops interrupts pended while the 4551da177e4SLinus Torvalds * corresponding pin was masked, so we can't know if an 4561da177e4SLinus Torvalds * interrupt is pending already. Let's hope not... 4571da177e4SLinus Torvalds */ 4581da177e4SLinus Torvalds return 0; 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds static void 4621da177e4SLinus Torvalds iosapic_ack_edge_irq (unsigned int irq) 4631da177e4SLinus Torvalds { 464a8553acdSIngo Molnar irq_desc_t *idesc = irq_desc + irq; 4651da177e4SLinus Torvalds 46641503defSChen, Kenneth W move_native_irq(irq); 4671da177e4SLinus Torvalds /* 4681da177e4SLinus Torvalds * Once we have recorded IRQ_PENDING already, we can mask the 4691da177e4SLinus Torvalds * interrupt for real. This prevents IRQ storms from unhandled 4701da177e4SLinus Torvalds * devices. 4711da177e4SLinus Torvalds */ 47246cba3dcSSatoru Takeuchi if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == 47346cba3dcSSatoru Takeuchi (IRQ_PENDING|IRQ_DISABLED)) 4741da177e4SLinus Torvalds mask_irq(irq); 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds #define iosapic_enable_edge_irq unmask_irq 4781da177e4SLinus Torvalds #define iosapic_disable_edge_irq nop 4791da177e4SLinus Torvalds #define iosapic_end_edge_irq nop 4801da177e4SLinus Torvalds 481e253eb0cSKAMEZAWA Hiroyuki struct irq_chip irq_type_iosapic_edge = { 48206344db3SIngo Molnar .name = "IO-SAPIC-edge", 4831da177e4SLinus Torvalds .startup = iosapic_startup_edge_irq, 4841da177e4SLinus Torvalds .shutdown = iosapic_disable_edge_irq, 4851da177e4SLinus Torvalds .enable = iosapic_enable_edge_irq, 4861da177e4SLinus Torvalds .disable = iosapic_disable_edge_irq, 4871da177e4SLinus Torvalds .ack = iosapic_ack_edge_irq, 4881da177e4SLinus Torvalds .end = iosapic_end_edge_irq, 489e253eb0cSKAMEZAWA Hiroyuki .mask = mask_irq, 490e253eb0cSKAMEZAWA Hiroyuki .unmask = unmask_irq, 4911da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4921da177e4SLinus Torvalds }; 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds unsigned int 4951da177e4SLinus Torvalds iosapic_version (char __iomem *addr) 4961da177e4SLinus Torvalds { 4971da177e4SLinus Torvalds /* 4981da177e4SLinus Torvalds * IOSAPIC Version Register return 32 bit structure like: 4991da177e4SLinus Torvalds * { 5001da177e4SLinus Torvalds * unsigned int version : 8; 5011da177e4SLinus Torvalds * unsigned int reserved1 : 8; 5021da177e4SLinus Torvalds * unsigned int max_redir : 8; 5031da177e4SLinus Torvalds * unsigned int reserved2 : 8; 5041da177e4SLinus Torvalds * } 5051da177e4SLinus Torvalds */ 506c1726d6fSYasuaki Ishimatsu return __iosapic_read(addr, IOSAPIC_VERSION); 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 5094bbdec7aSYasuaki Ishimatsu static int iosapic_find_sharable_irq(unsigned long trigger, unsigned long pol) 51024eeb568SKenji Kaneshige { 5114bbdec7aSYasuaki Ishimatsu int i, irq = -ENOSPC, min_count = -1; 51224eeb568SKenji Kaneshige struct iosapic_intr_info *info; 51324eeb568SKenji Kaneshige 51424eeb568SKenji Kaneshige /* 51524eeb568SKenji Kaneshige * shared vectors for edge-triggered interrupts are not 51624eeb568SKenji Kaneshige * supported yet 51724eeb568SKenji Kaneshige */ 51824eeb568SKenji Kaneshige if (trigger == IOSAPIC_EDGE) 51940598cbeSYasuaki Ishimatsu return -EINVAL; 52024eeb568SKenji Kaneshige 5214bbdec7aSYasuaki Ishimatsu for (i = 0; i <= NR_IRQS; i++) { 52224eeb568SKenji Kaneshige info = &iosapic_intr_info[i]; 52324eeb568SKenji Kaneshige if (info->trigger == trigger && info->polarity == pol && 524f8c087f3SYasuaki Ishimatsu (info->dmode == IOSAPIC_FIXED || 525f8c087f3SYasuaki Ishimatsu info->dmode == IOSAPIC_LOWEST_PRIORITY) && 526f8c087f3SYasuaki Ishimatsu can_request_irq(i, IRQF_SHARED)) { 52724eeb568SKenji Kaneshige if (min_count == -1 || info->count < min_count) { 5284bbdec7aSYasuaki Ishimatsu irq = i; 52924eeb568SKenji Kaneshige min_count = info->count; 53024eeb568SKenji Kaneshige } 53124eeb568SKenji Kaneshige } 53224eeb568SKenji Kaneshige } 5334bbdec7aSYasuaki Ishimatsu return irq; 53424eeb568SKenji Kaneshige } 53524eeb568SKenji Kaneshige 5361da177e4SLinus Torvalds /* 5371da177e4SLinus Torvalds * if the given vector is already owned by other, 5381da177e4SLinus Torvalds * assign a new vector for the other and make the vector available 5391da177e4SLinus Torvalds */ 5401da177e4SLinus Torvalds static void __init 5414bbdec7aSYasuaki Ishimatsu iosapic_reassign_vector (int irq) 5421da177e4SLinus Torvalds { 5434bbdec7aSYasuaki Ishimatsu int new_irq; 5441da177e4SLinus Torvalds 545c4c376f7SKenji Kaneshige if (iosapic_intr_info[irq].count) { 5464bbdec7aSYasuaki Ishimatsu new_irq = create_irq(); 5474bbdec7aSYasuaki Ishimatsu if (new_irq < 0) 5483b5cc090SKenji Kaneshige panic("%s: out of interrupt vectors!\n", __FUNCTION__); 54946cba3dcSSatoru Takeuchi printk(KERN_INFO "Reassigning vector %d to %d\n", 5504bbdec7aSYasuaki Ishimatsu irq_to_vector(irq), irq_to_vector(new_irq)); 5514bbdec7aSYasuaki Ishimatsu memcpy(&iosapic_intr_info[new_irq], &iosapic_intr_info[irq], 5521da177e4SLinus Torvalds sizeof(struct iosapic_intr_info)); 5534bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[new_irq].rtes); 5544bbdec7aSYasuaki Ishimatsu list_move(iosapic_intr_info[irq].rtes.next, 5554bbdec7aSYasuaki Ishimatsu &iosapic_intr_info[new_irq].rtes); 5564bbdec7aSYasuaki Ishimatsu memset(&iosapic_intr_info[irq], 0, 55746cba3dcSSatoru Takeuchi sizeof(struct iosapic_intr_info)); 5584bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = IOSAPIC_MASK; 5594bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[irq].rtes); 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 563056e6d89SSam Ravnborg static struct iosapic_rte_info * __init_refok iosapic_alloc_rte (void) 56424eeb568SKenji Kaneshige { 56524eeb568SKenji Kaneshige int i; 56624eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 56724eeb568SKenji Kaneshige int preallocated = 0; 56824eeb568SKenji Kaneshige 56924eeb568SKenji Kaneshige if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { 57046cba3dcSSatoru Takeuchi rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * 57146cba3dcSSatoru Takeuchi NR_PREALLOCATE_RTE_ENTRIES); 57224eeb568SKenji Kaneshige if (!rte) 57324eeb568SKenji Kaneshige return NULL; 57424eeb568SKenji Kaneshige for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) 57524eeb568SKenji Kaneshige list_add(&rte->rte_list, &free_rte_list); 57624eeb568SKenji Kaneshige } 57724eeb568SKenji Kaneshige 57824eeb568SKenji Kaneshige if (!list_empty(&free_rte_list)) { 57946cba3dcSSatoru Takeuchi rte = list_entry(free_rte_list.next, struct iosapic_rte_info, 58046cba3dcSSatoru Takeuchi rte_list); 58124eeb568SKenji Kaneshige list_del(&rte->rte_list); 58224eeb568SKenji Kaneshige preallocated++; 58324eeb568SKenji Kaneshige } else { 58424eeb568SKenji Kaneshige rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC); 58524eeb568SKenji Kaneshige if (!rte) 58624eeb568SKenji Kaneshige return NULL; 58724eeb568SKenji Kaneshige } 58824eeb568SKenji Kaneshige 58924eeb568SKenji Kaneshige memset(rte, 0, sizeof(struct iosapic_rte_info)); 59024eeb568SKenji Kaneshige if (preallocated) 59124eeb568SKenji Kaneshige rte->flags |= RTE_PREALLOCATED; 59224eeb568SKenji Kaneshige 59324eeb568SKenji Kaneshige return rte; 59424eeb568SKenji Kaneshige } 59524eeb568SKenji Kaneshige 5964bbdec7aSYasuaki Ishimatsu static inline int irq_is_shared (int irq) 59724eeb568SKenji Kaneshige { 5984bbdec7aSYasuaki Ishimatsu return (iosapic_intr_info[irq].count > 1); 59924eeb568SKenji Kaneshige } 60024eeb568SKenji Kaneshige 60114454a1bSKenji Kaneshige static int 6024bbdec7aSYasuaki Ishimatsu register_intr (unsigned int gsi, int irq, unsigned char delivery, 6031da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 6041da177e4SLinus Torvalds { 6051da177e4SLinus Torvalds irq_desc_t *idesc; 6061da177e4SLinus Torvalds struct hw_interrupt_type *irq_type; 6071da177e4SLinus Torvalds int index; 60824eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds index = find_iosapic(gsi); 6111da177e4SLinus Torvalds if (index < 0) { 61246cba3dcSSatoru Takeuchi printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", 61346cba3dcSSatoru Takeuchi __FUNCTION__, gsi); 61414454a1bSKenji Kaneshige return -ENODEV; 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds 6174bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 61824eeb568SKenji Kaneshige if (!rte) { 61924eeb568SKenji Kaneshige rte = iosapic_alloc_rte(); 62024eeb568SKenji Kaneshige if (!rte) { 62146cba3dcSSatoru Takeuchi printk(KERN_WARNING "%s: cannot allocate memory\n", 62246cba3dcSSatoru Takeuchi __FUNCTION__); 62314454a1bSKenji Kaneshige return -ENOMEM; 62424eeb568SKenji Kaneshige } 62524eeb568SKenji Kaneshige 626c5e3f9e5SYasuaki Ishimatsu rte->iosapic = &iosapic_lists[index]; 627c5e3f9e5SYasuaki Ishimatsu rte->rte_index = gsi - rte->iosapic->gsi_base; 62824eeb568SKenji Kaneshige rte->refcnt++; 6294bbdec7aSYasuaki Ishimatsu list_add_tail(&rte->rte_list, &iosapic_intr_info[irq].rtes); 6304bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].count++; 6310e888adcSKenji Kaneshige iosapic_lists[index].rtes_inuse++; 63224eeb568SKenji Kaneshige } 633e1b30a39SYasuaki Ishimatsu else if (rte->refcnt == NO_REF_RTE) { 6344bbdec7aSYasuaki Ishimatsu struct iosapic_intr_info *info = &iosapic_intr_info[irq]; 635e1b30a39SYasuaki Ishimatsu if (info->count > 0 && 636e1b30a39SYasuaki Ishimatsu (info->trigger != trigger || info->polarity != polarity)){ 63746cba3dcSSatoru Takeuchi printk (KERN_WARNING 63846cba3dcSSatoru Takeuchi "%s: cannot override the interrupt\n", 63946cba3dcSSatoru Takeuchi __FUNCTION__); 64014454a1bSKenji Kaneshige return -EINVAL; 64124eeb568SKenji Kaneshige } 642e1b30a39SYasuaki Ishimatsu rte->refcnt++; 643e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].count++; 644e1b30a39SYasuaki Ishimatsu iosapic_lists[index].rtes_inuse++; 64524eeb568SKenji Kaneshige } 64624eeb568SKenji Kaneshige 6474bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].polarity = polarity; 6484bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dmode = delivery; 6494bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].trigger = trigger; 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds if (trigger == IOSAPIC_EDGE) 6521da177e4SLinus Torvalds irq_type = &irq_type_iosapic_edge; 6531da177e4SLinus Torvalds else 6541da177e4SLinus Torvalds irq_type = &irq_type_iosapic_level; 6551da177e4SLinus Torvalds 6564bbdec7aSYasuaki Ishimatsu idesc = irq_desc + irq; 657d1bef4edSIngo Molnar if (idesc->chip != irq_type) { 658d1bef4edSIngo Molnar if (idesc->chip != &no_irq_type) 65946cba3dcSSatoru Takeuchi printk(KERN_WARNING 66046cba3dcSSatoru Takeuchi "%s: changing vector %d from %s to %s\n", 6614bbdec7aSYasuaki Ishimatsu __FUNCTION__, irq_to_vector(irq), 662351a5839SAndrew Morton idesc->chip->name, irq_type->name); 663d1bef4edSIngo Molnar idesc->chip = irq_type; 6641da177e4SLinus Torvalds } 66514454a1bSKenji Kaneshige return 0; 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds static unsigned int 6694bbdec7aSYasuaki Ishimatsu get_target_cpu (unsigned int gsi, int irq) 6701da177e4SLinus Torvalds { 6711da177e4SLinus Torvalds #ifdef CONFIG_SMP 6721da177e4SLinus Torvalds static int cpu = -1; 673ff741906SAshok Raj extern int cpe_vector; 6744994be1bSYasuaki Ishimatsu cpumask_t domain = irq_to_domain(irq); 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds /* 67724eeb568SKenji Kaneshige * In case of vector shared by multiple RTEs, all RTEs that 67824eeb568SKenji Kaneshige * share the vector need to use the same destination CPU. 67924eeb568SKenji Kaneshige */ 680c4c376f7SKenji Kaneshige if (iosapic_intr_info[irq].count) 6814bbdec7aSYasuaki Ishimatsu return iosapic_intr_info[irq].dest; 68224eeb568SKenji Kaneshige 68324eeb568SKenji Kaneshige /* 6841da177e4SLinus Torvalds * If the platform supports redirection via XTP, let it 6851da177e4SLinus Torvalds * distribute interrupts. 6861da177e4SLinus Torvalds */ 6871da177e4SLinus Torvalds if (smp_int_redirect & SMP_IRQ_REDIRECTION) 6881da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds /* 6911da177e4SLinus Torvalds * Some interrupts (ACPI SCI, for instance) are registered 6921da177e4SLinus Torvalds * before the BSP is marked as online. 6931da177e4SLinus Torvalds */ 6941da177e4SLinus Torvalds if (!cpu_online(smp_processor_id())) 6951da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6961da177e4SLinus Torvalds 697ff741906SAshok Raj #ifdef CONFIG_ACPI 6984bbdec7aSYasuaki Ishimatsu if (cpe_vector > 0 && irq_to_vector(irq) == IA64_CPEP_VECTOR) 699ff741906SAshok Raj return get_cpei_target_cpu(); 700ff741906SAshok Raj #endif 701ff741906SAshok Raj 7021da177e4SLinus Torvalds #ifdef CONFIG_NUMA 7031da177e4SLinus Torvalds { 7041da177e4SLinus Torvalds int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0; 7051da177e4SLinus Torvalds cpumask_t cpu_mask; 7061da177e4SLinus Torvalds 7071da177e4SLinus Torvalds iosapic_index = find_iosapic(gsi); 7081da177e4SLinus Torvalds if (iosapic_index < 0 || 7091da177e4SLinus Torvalds iosapic_lists[iosapic_index].node == MAX_NUMNODES) 7101da177e4SLinus Torvalds goto skip_numa_setup; 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node); 7134994be1bSYasuaki Ishimatsu cpus_and(cpu_mask, cpu_mask, domain); 7141da177e4SLinus Torvalds for_each_cpu_mask(numa_cpu, cpu_mask) { 7151da177e4SLinus Torvalds if (!cpu_online(numa_cpu)) 7161da177e4SLinus Torvalds cpu_clear(numa_cpu, cpu_mask); 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds 7191da177e4SLinus Torvalds num_cpus = cpus_weight(cpu_mask); 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds if (!num_cpus) 7221da177e4SLinus Torvalds goto skip_numa_setup; 7231da177e4SLinus Torvalds 7244bbdec7aSYasuaki Ishimatsu /* Use irq assignment to distribute across cpus in node */ 7254bbdec7aSYasuaki Ishimatsu cpu_index = irq % num_cpus; 7261da177e4SLinus Torvalds 7271da177e4SLinus Torvalds for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++) 7281da177e4SLinus Torvalds numa_cpu = next_cpu(numa_cpu, cpu_mask); 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds if (numa_cpu != NR_CPUS) 7311da177e4SLinus Torvalds return cpu_physical_id(numa_cpu); 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds skip_numa_setup: 7341da177e4SLinus Torvalds #endif 7351da177e4SLinus Torvalds /* 7361da177e4SLinus Torvalds * Otherwise, round-robin interrupt vectors across all the 7371da177e4SLinus Torvalds * processors. (It'd be nice if we could be smarter in the 7381da177e4SLinus Torvalds * case of NUMA.) 7391da177e4SLinus Torvalds */ 7401da177e4SLinus Torvalds do { 7411da177e4SLinus Torvalds if (++cpu >= NR_CPUS) 7421da177e4SLinus Torvalds cpu = 0; 7434994be1bSYasuaki Ishimatsu } while (!cpu_online(cpu) || !cpu_isset(cpu, domain)); 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds return cpu_physical_id(cpu); 74646cba3dcSSatoru Takeuchi #else /* CONFIG_SMP */ 7471da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 7481da177e4SLinus Torvalds #endif 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds /* 7521da177e4SLinus Torvalds * ACPI can describe IOSAPIC interrupts via static tables and namespace 7531da177e4SLinus Torvalds * methods. This provides an interface to register those interrupts and 7541da177e4SLinus Torvalds * program the IOSAPIC RTE. 7551da177e4SLinus Torvalds */ 7561da177e4SLinus Torvalds int 7571da177e4SLinus Torvalds iosapic_register_intr (unsigned int gsi, 7581da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 7591da177e4SLinus Torvalds { 7604bbdec7aSYasuaki Ishimatsu int irq, mask = 1, err; 7611da177e4SLinus Torvalds unsigned int dest; 7621da177e4SLinus Torvalds unsigned long flags; 76324eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 76424eeb568SKenji Kaneshige u32 low32; 76540598cbeSYasuaki Ishimatsu 7661da177e4SLinus Torvalds /* 7671da177e4SLinus Torvalds * If this GSI has already been registered (i.e., it's a 7681da177e4SLinus Torvalds * shared interrupt, or we lost a race to register it), 7691da177e4SLinus Torvalds * don't touch the RTE. 7701da177e4SLinus Torvalds */ 7711da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 7724bbdec7aSYasuaki Ishimatsu irq = __gsi_to_irq(gsi); 7734bbdec7aSYasuaki Ishimatsu if (irq > 0) { 7744bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 775e1b30a39SYasuaki Ishimatsu if(iosapic_intr_info[irq].count == 0) { 776e1b30a39SYasuaki Ishimatsu assign_irq_vector(irq); 777e1b30a39SYasuaki Ishimatsu dynamic_irq_init(irq); 778e1b30a39SYasuaki Ishimatsu } else if (rte->refcnt != NO_REF_RTE) { 77924eeb568SKenji Kaneshige rte->refcnt++; 78040598cbeSYasuaki Ishimatsu goto unlock_iosapic_lock; 7811da177e4SLinus Torvalds } 782e1b30a39SYasuaki Ishimatsu } else 783e1b30a39SYasuaki Ishimatsu irq = create_irq(); 7841da177e4SLinus Torvalds 78524eeb568SKenji Kaneshige /* If vector is running out, we try to find a sharable vector */ 786eb21ab24SYasuaki Ishimatsu if (irq < 0) { 7874bbdec7aSYasuaki Ishimatsu irq = iosapic_find_sharable_irq(trigger, polarity); 7884bbdec7aSYasuaki Ishimatsu if (irq < 0) 78940598cbeSYasuaki Ishimatsu goto unlock_iosapic_lock; 7904bbdec7aSYasuaki Ishimatsu } 79124eeb568SKenji Kaneshige 7924bbdec7aSYasuaki Ishimatsu spin_lock(&irq_desc[irq].lock); 7934bbdec7aSYasuaki Ishimatsu dest = get_target_cpu(gsi, irq); 7944bbdec7aSYasuaki Ishimatsu err = register_intr(gsi, irq, IOSAPIC_LOWEST_PRIORITY, 7951da177e4SLinus Torvalds polarity, trigger); 79614454a1bSKenji Kaneshige if (err < 0) { 797224685c0SKenji Kaneshige spin_unlock(&irq_desc[irq].lock); 7984bbdec7aSYasuaki Ishimatsu irq = err; 799224685c0SKenji Kaneshige goto unlock_iosapic_lock; 80014454a1bSKenji Kaneshige } 8011da177e4SLinus Torvalds 80224eeb568SKenji Kaneshige /* 803e3a8f7b8SYasuaki Ishimatsu * If the vector is shared and already unmasked for other 804e3a8f7b8SYasuaki Ishimatsu * interrupt sources, don't mask it. 80524eeb568SKenji Kaneshige */ 8064bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32; 8074bbdec7aSYasuaki Ishimatsu if (irq_is_shared(irq) && !(low32 & IOSAPIC_MASK)) 80824eeb568SKenji Kaneshige mask = 0; 8094bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, mask); 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", 8121da177e4SLinus Torvalds gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 8131da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 8144bbdec7aSYasuaki Ishimatsu cpu_logical_id(dest), dest, irq_to_vector(irq)); 815224685c0SKenji Kaneshige 8164bbdec7aSYasuaki Ishimatsu spin_unlock(&irq_desc[irq].lock); 81740598cbeSYasuaki Ishimatsu unlock_iosapic_lock: 81840598cbeSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 8194bbdec7aSYasuaki Ishimatsu return irq; 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds void 8231da177e4SLinus Torvalds iosapic_unregister_intr (unsigned int gsi) 8241da177e4SLinus Torvalds { 8251da177e4SLinus Torvalds unsigned long flags; 8264bbdec7aSYasuaki Ishimatsu int irq, index; 8271da177e4SLinus Torvalds irq_desc_t *idesc; 82824eeb568SKenji Kaneshige u32 low32; 8291da177e4SLinus Torvalds unsigned long trigger, polarity; 83024eeb568SKenji Kaneshige unsigned int dest; 83124eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 8321da177e4SLinus Torvalds 8331da177e4SLinus Torvalds /* 8341da177e4SLinus Torvalds * If the irq associated with the gsi is not found, 8351da177e4SLinus Torvalds * iosapic_unregister_intr() is unbalanced. We need to check 8361da177e4SLinus Torvalds * this again after getting locks. 8371da177e4SLinus Torvalds */ 8381da177e4SLinus Torvalds irq = gsi_to_irq(gsi); 8391da177e4SLinus Torvalds if (irq < 0) { 84046cba3dcSSatoru Takeuchi printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", 84146cba3dcSSatoru Takeuchi gsi); 8421da177e4SLinus Torvalds WARN_ON(1); 8431da177e4SLinus Torvalds return; 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds 84640598cbeSYasuaki Ishimatsu spin_lock_irqsave(&iosapic_lock, flags); 8474bbdec7aSYasuaki Ishimatsu if ((rte = find_rte(irq, gsi)) == NULL) { 848e3a8f7b8SYasuaki Ishimatsu printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", 84946cba3dcSSatoru Takeuchi gsi); 8501da177e4SLinus Torvalds WARN_ON(1); 85124eeb568SKenji Kaneshige goto out; 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds 85424eeb568SKenji Kaneshige if (--rte->refcnt > 0) 85524eeb568SKenji Kaneshige goto out; 8561da177e4SLinus Torvalds 85740598cbeSYasuaki Ishimatsu idesc = irq_desc + irq; 858e1b30a39SYasuaki Ishimatsu rte->refcnt = NO_REF_RTE; 85940598cbeSYasuaki Ishimatsu 86024eeb568SKenji Kaneshige /* Mask the interrupt */ 8614bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 | IOSAPIC_MASK; 862c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte->rte_index), low32); 8631da177e4SLinus Torvalds 8644bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].count--; 8650e888adcSKenji Kaneshige index = find_iosapic(gsi); 8660e888adcSKenji Kaneshige iosapic_lists[index].rtes_inuse--; 8670e888adcSKenji Kaneshige WARN_ON(iosapic_lists[index].rtes_inuse < 0); 8681da177e4SLinus Torvalds 8694bbdec7aSYasuaki Ishimatsu trigger = iosapic_intr_info[irq].trigger; 8704bbdec7aSYasuaki Ishimatsu polarity = iosapic_intr_info[irq].polarity; 8714bbdec7aSYasuaki Ishimatsu dest = iosapic_intr_info[irq].dest; 87246cba3dcSSatoru Takeuchi printk(KERN_INFO 873e3a8f7b8SYasuaki Ishimatsu "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n", 87424eeb568SKenji Kaneshige gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 87524eeb568SKenji Kaneshige (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 8764bbdec7aSYasuaki Ishimatsu cpu_logical_id(dest), dest, irq_to_vector(irq)); 8771da177e4SLinus Torvalds 878e1b30a39SYasuaki Ishimatsu if (iosapic_intr_info[irq].count == 0) { 879451fe00cSAlex Williamson #ifdef CONFIG_SMP 880451fe00cSAlex Williamson /* Clear affinity */ 881451fe00cSAlex Williamson cpus_setall(idesc->affinity); 882451fe00cSAlex Williamson #endif 88324eeb568SKenji Kaneshige /* Clear the interrupt information */ 884e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].dest = 0; 885e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].dmode = 0; 886e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].polarity = 0; 887e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].trigger = 0; 8884bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 |= IOSAPIC_MASK; 88924eeb568SKenji Kaneshige 890e1b30a39SYasuaki Ishimatsu /* Destroy and reserve IRQ */ 891e1b30a39SYasuaki Ishimatsu destroy_and_reserve_irq(irq); 89224eeb568SKenji Kaneshige } 89324eeb568SKenji Kaneshige out: 89440598cbeSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds /* 8981da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a platform interrupt. 8991da177e4SLinus Torvalds */ 9001da177e4SLinus Torvalds int __init 9011da177e4SLinus Torvalds iosapic_register_platform_intr (u32 int_type, unsigned int gsi, 9021da177e4SLinus Torvalds int iosapic_vector, u16 eid, u16 id, 9031da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 9041da177e4SLinus Torvalds { 9051da177e4SLinus Torvalds static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"}; 9061da177e4SLinus Torvalds unsigned char delivery; 907eb21ab24SYasuaki Ishimatsu int irq, vector, mask = 0; 9081da177e4SLinus Torvalds unsigned int dest = ((id << 8) | eid) & 0xffff; 9091da177e4SLinus Torvalds 9101da177e4SLinus Torvalds switch (int_type) { 9111da177e4SLinus Torvalds case ACPI_INTERRUPT_PMI: 912e1b30a39SYasuaki Ishimatsu irq = vector = iosapic_vector; 9134994be1bSYasuaki Ishimatsu bind_irq_vector(irq, vector, CPU_MASK_ALL); 9141da177e4SLinus Torvalds /* 9151da177e4SLinus Torvalds * since PMI vector is alloc'd by FW(ACPI) not by kernel, 9161da177e4SLinus Torvalds * we need to make sure the vector is available 9171da177e4SLinus Torvalds */ 9184bbdec7aSYasuaki Ishimatsu iosapic_reassign_vector(irq); 9191da177e4SLinus Torvalds delivery = IOSAPIC_PMI; 9201da177e4SLinus Torvalds break; 9211da177e4SLinus Torvalds case ACPI_INTERRUPT_INIT: 922eb21ab24SYasuaki Ishimatsu irq = create_irq(); 923eb21ab24SYasuaki Ishimatsu if (irq < 0) 9243b5cc090SKenji Kaneshige panic("%s: out of interrupt vectors!\n", __FUNCTION__); 925eb21ab24SYasuaki Ishimatsu vector = irq_to_vector(irq); 9261da177e4SLinus Torvalds delivery = IOSAPIC_INIT; 9271da177e4SLinus Torvalds break; 9281da177e4SLinus Torvalds case ACPI_INTERRUPT_CPEI: 929e1b30a39SYasuaki Ishimatsu irq = vector = IA64_CPE_VECTOR; 9304994be1bSYasuaki Ishimatsu BUG_ON(bind_irq_vector(irq, vector, CPU_MASK_ALL)); 9311da177e4SLinus Torvalds delivery = IOSAPIC_LOWEST_PRIORITY; 9321da177e4SLinus Torvalds mask = 1; 9331da177e4SLinus Torvalds break; 9341da177e4SLinus Torvalds default: 93546cba3dcSSatoru Takeuchi printk(KERN_ERR "%s: invalid int type 0x%x\n", __FUNCTION__, 93646cba3dcSSatoru Takeuchi int_type); 9371da177e4SLinus Torvalds return -1; 9381da177e4SLinus Torvalds } 9391da177e4SLinus Torvalds 9404bbdec7aSYasuaki Ishimatsu register_intr(gsi, irq, delivery, polarity, trigger); 9411da177e4SLinus Torvalds 94246cba3dcSSatoru Takeuchi printk(KERN_INFO 94346cba3dcSSatoru Takeuchi "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x)" 94446cba3dcSSatoru Takeuchi " vector %d\n", 9451da177e4SLinus Torvalds int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown", 9461da177e4SLinus Torvalds int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 9471da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 9481da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9491da177e4SLinus Torvalds 9504bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, mask); 9511da177e4SLinus Torvalds return vector; 9521da177e4SLinus Torvalds } 9531da177e4SLinus Torvalds 9541da177e4SLinus Torvalds /* 9551da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a legacy ISA IRQ override. 9561da177e4SLinus Torvalds */ 9570f7ac29eSTony Luck void __devinit 9581da177e4SLinus Torvalds iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, 9591da177e4SLinus Torvalds unsigned long polarity, 9601da177e4SLinus Torvalds unsigned long trigger) 9611da177e4SLinus Torvalds { 9624bbdec7aSYasuaki Ishimatsu int vector, irq; 9631da177e4SLinus Torvalds unsigned int dest = cpu_physical_id(smp_processor_id()); 9641da177e4SLinus Torvalds 965e1b30a39SYasuaki Ishimatsu irq = vector = isa_irq_to_vector(isa_irq); 9664994be1bSYasuaki Ishimatsu BUG_ON(bind_irq_vector(irq, vector, CPU_MASK_ALL)); 9674bbdec7aSYasuaki Ishimatsu register_intr(gsi, irq, IOSAPIC_LOWEST_PRIORITY, polarity, trigger); 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n", 9701da177e4SLinus Torvalds isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level", 9711da177e4SLinus Torvalds polarity == IOSAPIC_POL_HIGH ? "high" : "low", 9721da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9731da177e4SLinus Torvalds 9744bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, 1); 9751da177e4SLinus Torvalds } 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds void __init 9781da177e4SLinus Torvalds iosapic_system_init (int system_pcat_compat) 9791da177e4SLinus Torvalds { 9804bbdec7aSYasuaki Ishimatsu int irq; 9811da177e4SLinus Torvalds 9824bbdec7aSYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; ++irq) { 9834bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = IOSAPIC_MASK; 98446cba3dcSSatoru Takeuchi /* mark as unused */ 9854bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[irq].rtes); 986e1b30a39SYasuaki Ishimatsu 987e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].count = 0; 98824eeb568SKenji Kaneshige } 9891da177e4SLinus Torvalds 9901da177e4SLinus Torvalds pcat_compat = system_pcat_compat; 9911da177e4SLinus Torvalds if (pcat_compat) { 9921da177e4SLinus Torvalds /* 99346cba3dcSSatoru Takeuchi * Disable the compatibility mode interrupts (8259 style), 99446cba3dcSSatoru Takeuchi * needs IN/OUT support enabled. 9951da177e4SLinus Torvalds */ 99646cba3dcSSatoru Takeuchi printk(KERN_INFO 99746cba3dcSSatoru Takeuchi "%s: Disabling PC-AT compatible 8259 interrupts\n", 99846cba3dcSSatoru Takeuchi __FUNCTION__); 9991da177e4SLinus Torvalds outb(0xff, 0xA1); 10001da177e4SLinus Torvalds outb(0xff, 0x21); 10011da177e4SLinus Torvalds } 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 10040e888adcSKenji Kaneshige static inline int 10050e888adcSKenji Kaneshige iosapic_alloc (void) 10060e888adcSKenji Kaneshige { 10070e888adcSKenji Kaneshige int index; 10080e888adcSKenji Kaneshige 10090e888adcSKenji Kaneshige for (index = 0; index < NR_IOSAPICS; index++) 10100e888adcSKenji Kaneshige if (!iosapic_lists[index].addr) 10110e888adcSKenji Kaneshige return index; 10120e888adcSKenji Kaneshige 10130e888adcSKenji Kaneshige printk(KERN_WARNING "%s: failed to allocate iosapic\n", __FUNCTION__); 10140e888adcSKenji Kaneshige return -1; 10150e888adcSKenji Kaneshige } 10160e888adcSKenji Kaneshige 10170e888adcSKenji Kaneshige static inline void 10180e888adcSKenji Kaneshige iosapic_free (int index) 10190e888adcSKenji Kaneshige { 10200e888adcSKenji Kaneshige memset(&iosapic_lists[index], 0, sizeof(iosapic_lists[0])); 10210e888adcSKenji Kaneshige } 10220e888adcSKenji Kaneshige 10230e888adcSKenji Kaneshige static inline int 10240e888adcSKenji Kaneshige iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver) 10250e888adcSKenji Kaneshige { 10260e888adcSKenji Kaneshige int index; 10270e888adcSKenji Kaneshige unsigned int gsi_end, base, end; 10280e888adcSKenji Kaneshige 10290e888adcSKenji Kaneshige /* check gsi range */ 10300e888adcSKenji Kaneshige gsi_end = gsi_base + ((ver >> 16) & 0xff); 10310e888adcSKenji Kaneshige for (index = 0; index < NR_IOSAPICS; index++) { 10320e888adcSKenji Kaneshige if (!iosapic_lists[index].addr) 10330e888adcSKenji Kaneshige continue; 10340e888adcSKenji Kaneshige 10350e888adcSKenji Kaneshige base = iosapic_lists[index].gsi_base; 10360e888adcSKenji Kaneshige end = base + iosapic_lists[index].num_rte - 1; 10370e888adcSKenji Kaneshige 1038e6d1ba5cSSatoru Takeuchi if (gsi_end < base || end < gsi_base) 10390e888adcSKenji Kaneshige continue; /* OK */ 10400e888adcSKenji Kaneshige 10410e888adcSKenji Kaneshige return -EBUSY; 10420e888adcSKenji Kaneshige } 10430e888adcSKenji Kaneshige return 0; 10440e888adcSKenji Kaneshige } 10450e888adcSKenji Kaneshige 10460e888adcSKenji Kaneshige int __devinit 10471da177e4SLinus Torvalds iosapic_init (unsigned long phys_addr, unsigned int gsi_base) 10481da177e4SLinus Torvalds { 10490e888adcSKenji Kaneshige int num_rte, err, index; 10501da177e4SLinus Torvalds unsigned int isa_irq, ver; 10511da177e4SLinus Torvalds char __iomem *addr; 10520e888adcSKenji Kaneshige unsigned long flags; 10531da177e4SLinus Torvalds 10540e888adcSKenji Kaneshige spin_lock_irqsave(&iosapic_lock, flags); 1055c1726d6fSYasuaki Ishimatsu index = find_iosapic(gsi_base); 1056c1726d6fSYasuaki Ishimatsu if (index >= 0) { 1057c1726d6fSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 1058c1726d6fSYasuaki Ishimatsu return -EBUSY; 1059c1726d6fSYasuaki Ishimatsu } 1060c1726d6fSYasuaki Ishimatsu 10611da177e4SLinus Torvalds addr = ioremap(phys_addr, 0); 10621da177e4SLinus Torvalds ver = iosapic_version(addr); 10630e888adcSKenji Kaneshige if ((err = iosapic_check_gsi_range(gsi_base, ver))) { 10640e888adcSKenji Kaneshige iounmap(addr); 10650e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 10660e888adcSKenji Kaneshige return err; 10670e888adcSKenji Kaneshige } 10680e888adcSKenji Kaneshige 10691da177e4SLinus Torvalds /* 1070e3a8f7b8SYasuaki Ishimatsu * The MAX_REDIR register holds the highest input pin number 1071e3a8f7b8SYasuaki Ishimatsu * (starting from 0). We add 1 so that we can use it for 1072e3a8f7b8SYasuaki Ishimatsu * number of pins (= RTEs) 10731da177e4SLinus Torvalds */ 10741da177e4SLinus Torvalds num_rte = ((ver >> 16) & 0xff) + 1; 10751da177e4SLinus Torvalds 10760e888adcSKenji Kaneshige index = iosapic_alloc(); 10770e888adcSKenji Kaneshige iosapic_lists[index].addr = addr; 10780e888adcSKenji Kaneshige iosapic_lists[index].gsi_base = gsi_base; 10790e888adcSKenji Kaneshige iosapic_lists[index].num_rte = num_rte; 10801da177e4SLinus Torvalds #ifdef CONFIG_NUMA 10810e888adcSKenji Kaneshige iosapic_lists[index].node = MAX_NUMNODES; 10821da177e4SLinus Torvalds #endif 1083c1726d6fSYasuaki Ishimatsu spin_lock_init(&iosapic_lists[index].lock); 10840e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds if ((gsi_base == 0) && pcat_compat) { 10871da177e4SLinus Torvalds /* 108846cba3dcSSatoru Takeuchi * Map the legacy ISA devices into the IOSAPIC data. Some of 108946cba3dcSSatoru Takeuchi * these may get reprogrammed later on with data from the ACPI 109046cba3dcSSatoru Takeuchi * Interrupt Source Override table. 10911da177e4SLinus Torvalds */ 10921da177e4SLinus Torvalds for (isa_irq = 0; isa_irq < 16; ++isa_irq) 109346cba3dcSSatoru Takeuchi iosapic_override_isa_irq(isa_irq, isa_irq, 109446cba3dcSSatoru Takeuchi IOSAPIC_POL_HIGH, 109546cba3dcSSatoru Takeuchi IOSAPIC_EDGE); 10961da177e4SLinus Torvalds } 10970e888adcSKenji Kaneshige return 0; 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds 11000e888adcSKenji Kaneshige #ifdef CONFIG_HOTPLUG 11010e888adcSKenji Kaneshige int 11020e888adcSKenji Kaneshige iosapic_remove (unsigned int gsi_base) 11030e888adcSKenji Kaneshige { 11040e888adcSKenji Kaneshige int index, err = 0; 11050e888adcSKenji Kaneshige unsigned long flags; 11060e888adcSKenji Kaneshige 11070e888adcSKenji Kaneshige spin_lock_irqsave(&iosapic_lock, flags); 11080e888adcSKenji Kaneshige index = find_iosapic(gsi_base); 11090e888adcSKenji Kaneshige if (index < 0) { 11100e888adcSKenji Kaneshige printk(KERN_WARNING "%s: No IOSAPIC for GSI base %u\n", 11110e888adcSKenji Kaneshige __FUNCTION__, gsi_base); 11120e888adcSKenji Kaneshige goto out; 11130e888adcSKenji Kaneshige } 11140e888adcSKenji Kaneshige 11150e888adcSKenji Kaneshige if (iosapic_lists[index].rtes_inuse) { 11160e888adcSKenji Kaneshige err = -EBUSY; 1117e3a8f7b8SYasuaki Ishimatsu printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n", 11180e888adcSKenji Kaneshige __FUNCTION__, gsi_base); 11190e888adcSKenji Kaneshige goto out; 11200e888adcSKenji Kaneshige } 11210e888adcSKenji Kaneshige 11220e888adcSKenji Kaneshige iounmap(iosapic_lists[index].addr); 11230e888adcSKenji Kaneshige iosapic_free(index); 11240e888adcSKenji Kaneshige out: 11250e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 11260e888adcSKenji Kaneshige return err; 11270e888adcSKenji Kaneshige } 11280e888adcSKenji Kaneshige #endif /* CONFIG_HOTPLUG */ 11290e888adcSKenji Kaneshige 11301da177e4SLinus Torvalds #ifdef CONFIG_NUMA 11310e888adcSKenji Kaneshige void __devinit 11321da177e4SLinus Torvalds map_iosapic_to_node(unsigned int gsi_base, int node) 11331da177e4SLinus Torvalds { 11341da177e4SLinus Torvalds int index; 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds index = find_iosapic(gsi_base); 11371da177e4SLinus Torvalds if (index < 0) { 11381da177e4SLinus Torvalds printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", 11391da177e4SLinus Torvalds __FUNCTION__, gsi_base); 11401da177e4SLinus Torvalds return; 11411da177e4SLinus Torvalds } 11421da177e4SLinus Torvalds iosapic_lists[index].node = node; 11431da177e4SLinus Torvalds return; 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds #endif 114624eeb568SKenji Kaneshige 114724eeb568SKenji Kaneshige static int __init iosapic_enable_kmalloc (void) 114824eeb568SKenji Kaneshige { 114924eeb568SKenji Kaneshige iosapic_kmalloc_ok = 1; 115024eeb568SKenji Kaneshige return 0; 115124eeb568SKenji Kaneshige } 115224eeb568SKenji Kaneshige core_initcall (iosapic_enable_kmalloc); 1153