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 int 2031da177e4SLinus Torvalds gsi_to_irq (unsigned int gsi) 2041da177e4SLinus Torvalds { 20524eeb568SKenji Kaneshige unsigned long flags; 20624eeb568SKenji Kaneshige int irq; 20724eeb568SKenji Kaneshige 2084bbdec7aSYasuaki Ishimatsu spin_lock_irqsave(&iosapic_lock, flags); 2094bbdec7aSYasuaki Ishimatsu irq = __gsi_to_irq(gsi); 2104bbdec7aSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 21124eeb568SKenji Kaneshige return irq; 21224eeb568SKenji Kaneshige } 21324eeb568SKenji Kaneshige 2144bbdec7aSYasuaki Ishimatsu static struct iosapic_rte_info *find_rte(unsigned int irq, unsigned int gsi) 21524eeb568SKenji Kaneshige { 21624eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 21724eeb568SKenji Kaneshige 2184bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) 219c5e3f9e5SYasuaki Ishimatsu if (rte->iosapic->gsi_base + rte->rte_index == gsi) 22024eeb568SKenji Kaneshige return rte; 22124eeb568SKenji Kaneshige return NULL; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds static void 2254bbdec7aSYasuaki Ishimatsu set_rte (unsigned int gsi, unsigned int irq, unsigned int dest, int mask) 2261da177e4SLinus Torvalds { 2271da177e4SLinus Torvalds unsigned long pol, trigger, dmode; 2281da177e4SLinus Torvalds u32 low32, high32; 2291da177e4SLinus Torvalds int rte_index; 2301da177e4SLinus Torvalds char redir; 23124eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 2324bbdec7aSYasuaki Ishimatsu ia64_vector vector = irq_to_vector(irq); 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); 2351da177e4SLinus Torvalds 2364bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 23724eeb568SKenji Kaneshige if (!rte) 2381da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 2391da177e4SLinus Torvalds 24024eeb568SKenji Kaneshige rte_index = rte->rte_index; 2414bbdec7aSYasuaki Ishimatsu pol = iosapic_intr_info[irq].polarity; 2424bbdec7aSYasuaki Ishimatsu trigger = iosapic_intr_info[irq].trigger; 2434bbdec7aSYasuaki Ishimatsu dmode = iosapic_intr_info[irq].dmode; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds #ifdef CONFIG_SMP 2484bbdec7aSYasuaki Ishimatsu set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); 2491da177e4SLinus Torvalds #endif 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds low32 = ((pol << IOSAPIC_POLARITY_SHIFT) | 2521da177e4SLinus Torvalds (trigger << IOSAPIC_TRIGGER_SHIFT) | 2531da177e4SLinus Torvalds (dmode << IOSAPIC_DELIVERY_SHIFT) | 2541da177e4SLinus Torvalds ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) | 2551da177e4SLinus Torvalds vector); 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds /* dest contains both id and eid */ 2581da177e4SLinus Torvalds high32 = (dest << IOSAPIC_DEST_SHIFT); 2591da177e4SLinus Torvalds 260c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_HIGH(rte_index), high32); 261c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 2624bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = low32; 2634bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dest = dest; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds static void 26746cba3dcSSatoru Takeuchi nop (unsigned int irq) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds /* do nothing... */ 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 272a7956113SZou Nan hai 273a7956113SZou Nan hai #ifdef CONFIG_KEXEC 274a7956113SZou Nan hai void 275a7956113SZou Nan hai kexec_disable_iosapic(void) 276a7956113SZou Nan hai { 277a7956113SZou Nan hai struct iosapic_intr_info *info; 278a7956113SZou Nan hai struct iosapic_rte_info *rte; 2794bbdec7aSYasuaki Ishimatsu ia64_vector vec; 2804bbdec7aSYasuaki Ishimatsu int irq; 2814bbdec7aSYasuaki Ishimatsu 2824bbdec7aSYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; irq++) { 2834bbdec7aSYasuaki Ishimatsu info = &iosapic_intr_info[irq]; 2844bbdec7aSYasuaki Ishimatsu vec = irq_to_vector(irq); 285a7956113SZou Nan hai list_for_each_entry(rte, &info->rtes, 286a7956113SZou Nan hai rte_list) { 287c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, 288a7956113SZou Nan hai IOSAPIC_RTE_LOW(rte->rte_index), 289a7956113SZou Nan hai IOSAPIC_MASK|vec); 290c5e3f9e5SYasuaki Ishimatsu iosapic_eoi(rte->iosapic->addr, vec); 291a7956113SZou Nan hai } 292a7956113SZou Nan hai } 293a7956113SZou Nan hai } 294a7956113SZou Nan hai #endif 295a7956113SZou Nan hai 2961da177e4SLinus Torvalds static void 2971da177e4SLinus Torvalds mask_irq (unsigned int irq) 2981da177e4SLinus Torvalds { 2991da177e4SLinus Torvalds u32 low32; 3001da177e4SLinus Torvalds int rte_index; 30124eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3021da177e4SLinus Torvalds 303c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3041da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds /* set only the mask bit */ 3074bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 |= IOSAPIC_MASK; 3084bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 30924eeb568SKenji Kaneshige rte_index = rte->rte_index; 310c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds static void 3151da177e4SLinus Torvalds unmask_irq (unsigned int irq) 3161da177e4SLinus Torvalds { 3171da177e4SLinus Torvalds u32 low32; 3181da177e4SLinus Torvalds int rte_index; 31924eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3201da177e4SLinus Torvalds 321c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3221da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 3231da177e4SLinus Torvalds 3244bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 &= ~IOSAPIC_MASK; 3254bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 32624eeb568SKenji Kaneshige rte_index = rte->rte_index; 327c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds static void 3331da177e4SLinus Torvalds iosapic_set_affinity (unsigned int irq, cpumask_t mask) 3341da177e4SLinus Torvalds { 3351da177e4SLinus Torvalds #ifdef CONFIG_SMP 3361da177e4SLinus Torvalds u32 high32, low32; 3371da177e4SLinus Torvalds int dest, rte_index; 3381da177e4SLinus Torvalds int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; 33924eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 340c1726d6fSYasuaki Ishimatsu struct iosapic *iosapic; 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds irq &= (~IA64_IRQ_REDIRECTED); 3431da177e4SLinus Torvalds 344cd378f18SYasuaki Ishimatsu cpus_and(mask, mask, cpu_online_map); 3451da177e4SLinus Torvalds if (cpus_empty(mask)) 3461da177e4SLinus Torvalds return; 3471da177e4SLinus Torvalds 348a6cd6322SKenji Kaneshige if (irq_prepare_move(irq, first_cpu(mask))) 349cd378f18SYasuaki Ishimatsu return; 350cd378f18SYasuaki Ishimatsu 3511da177e4SLinus Torvalds dest = cpu_physical_id(first_cpu(mask)); 3521da177e4SLinus Torvalds 353c4c376f7SKenji Kaneshige if (!iosapic_intr_info[irq].count) 3541da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds set_irq_affinity_info(irq, dest, redir); 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds /* dest contains both id and eid */ 3591da177e4SLinus Torvalds high32 = dest << IOSAPIC_DEST_SHIFT; 3601da177e4SLinus Torvalds 3614bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); 3621da177e4SLinus Torvalds if (redir) 3631da177e4SLinus Torvalds /* change delivery mode to lowest priority */ 364e3a8f7b8SYasuaki Ishimatsu low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); 3651da177e4SLinus Torvalds else 3661da177e4SLinus Torvalds /* change delivery mode to fixed */ 3671da177e4SLinus Torvalds low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); 368cd378f18SYasuaki Ishimatsu low32 &= IOSAPIC_VECTOR_MASK; 369cd378f18SYasuaki Ishimatsu low32 |= irq_to_vector(irq); 3701da177e4SLinus Torvalds 3714bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = low32; 3724bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dest = dest; 3734bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) { 374c1726d6fSYasuaki Ishimatsu iosapic = rte->iosapic; 37524eeb568SKenji Kaneshige rte_index = rte->rte_index; 376c1726d6fSYasuaki Ishimatsu iosapic_write(iosapic, IOSAPIC_RTE_HIGH(rte_index), high32); 377c1726d6fSYasuaki Ishimatsu iosapic_write(iosapic, IOSAPIC_RTE_LOW(rte_index), low32); 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds #endif 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds /* 3831da177e4SLinus Torvalds * Handlers for level-triggered interrupts. 3841da177e4SLinus Torvalds */ 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds static unsigned int 3871da177e4SLinus Torvalds iosapic_startup_level_irq (unsigned int irq) 3881da177e4SLinus Torvalds { 3891da177e4SLinus Torvalds unmask_irq(irq); 3901da177e4SLinus Torvalds return 0; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds static void 3941da177e4SLinus Torvalds iosapic_end_level_irq (unsigned int irq) 3951da177e4SLinus Torvalds { 3961da177e4SLinus Torvalds ia64_vector vec = irq_to_vector(irq); 39724eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 398cd378f18SYasuaki Ishimatsu int do_unmask_irq = 0; 3991da177e4SLinus Torvalds 400a6cd6322SKenji Kaneshige irq_complete_move(irq); 401cd378f18SYasuaki Ishimatsu if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) { 402cd378f18SYasuaki Ishimatsu do_unmask_irq = 1; 403cd378f18SYasuaki Ishimatsu mask_irq(irq); 404cd378f18SYasuaki Ishimatsu } 405cd378f18SYasuaki Ishimatsu 4064bbdec7aSYasuaki Ishimatsu list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) 407c5e3f9e5SYasuaki Ishimatsu iosapic_eoi(rte->iosapic->addr, vec); 408cd378f18SYasuaki Ishimatsu 409cd378f18SYasuaki Ishimatsu if (unlikely(do_unmask_irq)) { 410cd378f18SYasuaki Ishimatsu move_masked_irq(irq); 411cd378f18SYasuaki Ishimatsu unmask_irq(irq); 412cd378f18SYasuaki Ishimatsu } 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds #define iosapic_shutdown_level_irq mask_irq 4161da177e4SLinus Torvalds #define iosapic_enable_level_irq unmask_irq 4171da177e4SLinus Torvalds #define iosapic_disable_level_irq mask_irq 4181da177e4SLinus Torvalds #define iosapic_ack_level_irq nop 4191da177e4SLinus Torvalds 4209e004ebdSSimon Horman static struct irq_chip irq_type_iosapic_level = { 42106344db3SIngo Molnar .name = "IO-SAPIC-level", 4221da177e4SLinus Torvalds .startup = iosapic_startup_level_irq, 4231da177e4SLinus Torvalds .shutdown = iosapic_shutdown_level_irq, 4241da177e4SLinus Torvalds .enable = iosapic_enable_level_irq, 4251da177e4SLinus Torvalds .disable = iosapic_disable_level_irq, 4261da177e4SLinus Torvalds .ack = iosapic_ack_level_irq, 4271da177e4SLinus Torvalds .end = iosapic_end_level_irq, 428e253eb0cSKAMEZAWA Hiroyuki .mask = mask_irq, 429e253eb0cSKAMEZAWA Hiroyuki .unmask = unmask_irq, 4301da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4311da177e4SLinus Torvalds }; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds /* 4341da177e4SLinus Torvalds * Handlers for edge-triggered interrupts. 4351da177e4SLinus Torvalds */ 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds static unsigned int 4381da177e4SLinus Torvalds iosapic_startup_edge_irq (unsigned int irq) 4391da177e4SLinus Torvalds { 4401da177e4SLinus Torvalds unmask_irq(irq); 4411da177e4SLinus Torvalds /* 4421da177e4SLinus Torvalds * IOSAPIC simply drops interrupts pended while the 4431da177e4SLinus Torvalds * corresponding pin was masked, so we can't know if an 4441da177e4SLinus Torvalds * interrupt is pending already. Let's hope not... 4451da177e4SLinus Torvalds */ 4461da177e4SLinus Torvalds return 0; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds static void 4501da177e4SLinus Torvalds iosapic_ack_edge_irq (unsigned int irq) 4511da177e4SLinus Torvalds { 452a8553acdSIngo Molnar irq_desc_t *idesc = irq_desc + irq; 4531da177e4SLinus Torvalds 454a6cd6322SKenji Kaneshige irq_complete_move(irq); 45541503defSChen, Kenneth W move_native_irq(irq); 4561da177e4SLinus Torvalds /* 4571da177e4SLinus Torvalds * Once we have recorded IRQ_PENDING already, we can mask the 4581da177e4SLinus Torvalds * interrupt for real. This prevents IRQ storms from unhandled 4591da177e4SLinus Torvalds * devices. 4601da177e4SLinus Torvalds */ 46146cba3dcSSatoru Takeuchi if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == 46246cba3dcSSatoru Takeuchi (IRQ_PENDING|IRQ_DISABLED)) 4631da177e4SLinus Torvalds mask_irq(irq); 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds #define iosapic_enable_edge_irq unmask_irq 4671da177e4SLinus Torvalds #define iosapic_disable_edge_irq nop 4681da177e4SLinus Torvalds #define iosapic_end_edge_irq nop 4691da177e4SLinus Torvalds 4709e004ebdSSimon Horman static struct irq_chip irq_type_iosapic_edge = { 47106344db3SIngo Molnar .name = "IO-SAPIC-edge", 4721da177e4SLinus Torvalds .startup = iosapic_startup_edge_irq, 4731da177e4SLinus Torvalds .shutdown = iosapic_disable_edge_irq, 4741da177e4SLinus Torvalds .enable = iosapic_enable_edge_irq, 4751da177e4SLinus Torvalds .disable = iosapic_disable_edge_irq, 4761da177e4SLinus Torvalds .ack = iosapic_ack_edge_irq, 4771da177e4SLinus Torvalds .end = iosapic_end_edge_irq, 478e253eb0cSKAMEZAWA Hiroyuki .mask = mask_irq, 479e253eb0cSKAMEZAWA Hiroyuki .unmask = unmask_irq, 4801da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4811da177e4SLinus Torvalds }; 4821da177e4SLinus Torvalds 4839e004ebdSSimon Horman static unsigned int 4841da177e4SLinus Torvalds iosapic_version (char __iomem *addr) 4851da177e4SLinus Torvalds { 4861da177e4SLinus Torvalds /* 4871da177e4SLinus Torvalds * IOSAPIC Version Register return 32 bit structure like: 4881da177e4SLinus Torvalds * { 4891da177e4SLinus Torvalds * unsigned int version : 8; 4901da177e4SLinus Torvalds * unsigned int reserved1 : 8; 4911da177e4SLinus Torvalds * unsigned int max_redir : 8; 4921da177e4SLinus Torvalds * unsigned int reserved2 : 8; 4931da177e4SLinus Torvalds * } 4941da177e4SLinus Torvalds */ 495c1726d6fSYasuaki Ishimatsu return __iosapic_read(addr, IOSAPIC_VERSION); 4961da177e4SLinus Torvalds } 4971da177e4SLinus Torvalds 4984bbdec7aSYasuaki Ishimatsu static int iosapic_find_sharable_irq(unsigned long trigger, unsigned long pol) 49924eeb568SKenji Kaneshige { 5004bbdec7aSYasuaki Ishimatsu int i, irq = -ENOSPC, min_count = -1; 50124eeb568SKenji Kaneshige struct iosapic_intr_info *info; 50224eeb568SKenji Kaneshige 50324eeb568SKenji Kaneshige /* 50424eeb568SKenji Kaneshige * shared vectors for edge-triggered interrupts are not 50524eeb568SKenji Kaneshige * supported yet 50624eeb568SKenji Kaneshige */ 50724eeb568SKenji Kaneshige if (trigger == IOSAPIC_EDGE) 50840598cbeSYasuaki Ishimatsu return -EINVAL; 50924eeb568SKenji Kaneshige 5104bbdec7aSYasuaki Ishimatsu for (i = 0; i <= NR_IRQS; i++) { 51124eeb568SKenji Kaneshige info = &iosapic_intr_info[i]; 51224eeb568SKenji Kaneshige if (info->trigger == trigger && info->polarity == pol && 513f8c087f3SYasuaki Ishimatsu (info->dmode == IOSAPIC_FIXED || 514f8c087f3SYasuaki Ishimatsu info->dmode == IOSAPIC_LOWEST_PRIORITY) && 515f8c087f3SYasuaki Ishimatsu can_request_irq(i, IRQF_SHARED)) { 51624eeb568SKenji Kaneshige if (min_count == -1 || info->count < min_count) { 5174bbdec7aSYasuaki Ishimatsu irq = i; 51824eeb568SKenji Kaneshige min_count = info->count; 51924eeb568SKenji Kaneshige } 52024eeb568SKenji Kaneshige } 52124eeb568SKenji Kaneshige } 5224bbdec7aSYasuaki Ishimatsu return irq; 52324eeb568SKenji Kaneshige } 52424eeb568SKenji Kaneshige 5251da177e4SLinus Torvalds /* 5261da177e4SLinus Torvalds * if the given vector is already owned by other, 5271da177e4SLinus Torvalds * assign a new vector for the other and make the vector available 5281da177e4SLinus Torvalds */ 5291da177e4SLinus Torvalds static void __init 5304bbdec7aSYasuaki Ishimatsu iosapic_reassign_vector (int irq) 5311da177e4SLinus Torvalds { 5324bbdec7aSYasuaki Ishimatsu int new_irq; 5331da177e4SLinus Torvalds 534c4c376f7SKenji Kaneshige if (iosapic_intr_info[irq].count) { 5354bbdec7aSYasuaki Ishimatsu new_irq = create_irq(); 5364bbdec7aSYasuaki Ishimatsu if (new_irq < 0) 5373b5cc090SKenji Kaneshige panic("%s: out of interrupt vectors!\n", __FUNCTION__); 53846cba3dcSSatoru Takeuchi printk(KERN_INFO "Reassigning vector %d to %d\n", 5394bbdec7aSYasuaki Ishimatsu irq_to_vector(irq), irq_to_vector(new_irq)); 5404bbdec7aSYasuaki Ishimatsu memcpy(&iosapic_intr_info[new_irq], &iosapic_intr_info[irq], 5411da177e4SLinus Torvalds sizeof(struct iosapic_intr_info)); 5424bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[new_irq].rtes); 5434bbdec7aSYasuaki Ishimatsu list_move(iosapic_intr_info[irq].rtes.next, 5444bbdec7aSYasuaki Ishimatsu &iosapic_intr_info[new_irq].rtes); 5454bbdec7aSYasuaki Ishimatsu memset(&iosapic_intr_info[irq], 0, 54646cba3dcSSatoru Takeuchi sizeof(struct iosapic_intr_info)); 5474bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = IOSAPIC_MASK; 5484bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[irq].rtes); 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds 552056e6d89SSam Ravnborg static struct iosapic_rte_info * __init_refok iosapic_alloc_rte (void) 55324eeb568SKenji Kaneshige { 55424eeb568SKenji Kaneshige int i; 55524eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 55624eeb568SKenji Kaneshige int preallocated = 0; 55724eeb568SKenji Kaneshige 55824eeb568SKenji Kaneshige if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { 55946cba3dcSSatoru Takeuchi rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * 56046cba3dcSSatoru Takeuchi NR_PREALLOCATE_RTE_ENTRIES); 56124eeb568SKenji Kaneshige if (!rte) 56224eeb568SKenji Kaneshige return NULL; 56324eeb568SKenji Kaneshige for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) 56424eeb568SKenji Kaneshige list_add(&rte->rte_list, &free_rte_list); 56524eeb568SKenji Kaneshige } 56624eeb568SKenji Kaneshige 56724eeb568SKenji Kaneshige if (!list_empty(&free_rte_list)) { 56846cba3dcSSatoru Takeuchi rte = list_entry(free_rte_list.next, struct iosapic_rte_info, 56946cba3dcSSatoru Takeuchi rte_list); 57024eeb568SKenji Kaneshige list_del(&rte->rte_list); 57124eeb568SKenji Kaneshige preallocated++; 57224eeb568SKenji Kaneshige } else { 57324eeb568SKenji Kaneshige rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC); 57424eeb568SKenji Kaneshige if (!rte) 57524eeb568SKenji Kaneshige return NULL; 57624eeb568SKenji Kaneshige } 57724eeb568SKenji Kaneshige 57824eeb568SKenji Kaneshige memset(rte, 0, sizeof(struct iosapic_rte_info)); 57924eeb568SKenji Kaneshige if (preallocated) 58024eeb568SKenji Kaneshige rte->flags |= RTE_PREALLOCATED; 58124eeb568SKenji Kaneshige 58224eeb568SKenji Kaneshige return rte; 58324eeb568SKenji Kaneshige } 58424eeb568SKenji Kaneshige 5854bbdec7aSYasuaki Ishimatsu static inline int irq_is_shared (int irq) 58624eeb568SKenji Kaneshige { 5874bbdec7aSYasuaki Ishimatsu return (iosapic_intr_info[irq].count > 1); 58824eeb568SKenji Kaneshige } 58924eeb568SKenji Kaneshige 59014454a1bSKenji Kaneshige static int 5914bbdec7aSYasuaki Ishimatsu register_intr (unsigned int gsi, int irq, unsigned char delivery, 5921da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 5931da177e4SLinus Torvalds { 5941da177e4SLinus Torvalds irq_desc_t *idesc; 5951da177e4SLinus Torvalds struct hw_interrupt_type *irq_type; 5961da177e4SLinus Torvalds int index; 59724eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds index = find_iosapic(gsi); 6001da177e4SLinus Torvalds if (index < 0) { 60146cba3dcSSatoru Takeuchi printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", 60246cba3dcSSatoru Takeuchi __FUNCTION__, gsi); 60314454a1bSKenji Kaneshige return -ENODEV; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 6064bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 60724eeb568SKenji Kaneshige if (!rte) { 60824eeb568SKenji Kaneshige rte = iosapic_alloc_rte(); 60924eeb568SKenji Kaneshige if (!rte) { 61046cba3dcSSatoru Takeuchi printk(KERN_WARNING "%s: cannot allocate memory\n", 61146cba3dcSSatoru Takeuchi __FUNCTION__); 61214454a1bSKenji Kaneshige return -ENOMEM; 61324eeb568SKenji Kaneshige } 61424eeb568SKenji Kaneshige 615c5e3f9e5SYasuaki Ishimatsu rte->iosapic = &iosapic_lists[index]; 616c5e3f9e5SYasuaki Ishimatsu rte->rte_index = gsi - rte->iosapic->gsi_base; 61724eeb568SKenji Kaneshige rte->refcnt++; 6184bbdec7aSYasuaki Ishimatsu list_add_tail(&rte->rte_list, &iosapic_intr_info[irq].rtes); 6194bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].count++; 6200e888adcSKenji Kaneshige iosapic_lists[index].rtes_inuse++; 62124eeb568SKenji Kaneshige } 622e1b30a39SYasuaki Ishimatsu else if (rte->refcnt == NO_REF_RTE) { 6234bbdec7aSYasuaki Ishimatsu struct iosapic_intr_info *info = &iosapic_intr_info[irq]; 624e1b30a39SYasuaki Ishimatsu if (info->count > 0 && 625e1b30a39SYasuaki Ishimatsu (info->trigger != trigger || info->polarity != polarity)){ 62646cba3dcSSatoru Takeuchi printk (KERN_WARNING 62746cba3dcSSatoru Takeuchi "%s: cannot override the interrupt\n", 62846cba3dcSSatoru Takeuchi __FUNCTION__); 62914454a1bSKenji Kaneshige return -EINVAL; 63024eeb568SKenji Kaneshige } 631e1b30a39SYasuaki Ishimatsu rte->refcnt++; 632e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].count++; 633e1b30a39SYasuaki Ishimatsu iosapic_lists[index].rtes_inuse++; 63424eeb568SKenji Kaneshige } 63524eeb568SKenji Kaneshige 6364bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].polarity = polarity; 6374bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].dmode = delivery; 6384bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].trigger = trigger; 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds if (trigger == IOSAPIC_EDGE) 6411da177e4SLinus Torvalds irq_type = &irq_type_iosapic_edge; 6421da177e4SLinus Torvalds else 6431da177e4SLinus Torvalds irq_type = &irq_type_iosapic_level; 6441da177e4SLinus Torvalds 6454bbdec7aSYasuaki Ishimatsu idesc = irq_desc + irq; 646d1bef4edSIngo Molnar if (idesc->chip != irq_type) { 647d1bef4edSIngo Molnar if (idesc->chip != &no_irq_type) 64846cba3dcSSatoru Takeuchi printk(KERN_WARNING 64946cba3dcSSatoru Takeuchi "%s: changing vector %d from %s to %s\n", 6504bbdec7aSYasuaki Ishimatsu __FUNCTION__, irq_to_vector(irq), 651351a5839SAndrew Morton idesc->chip->name, irq_type->name); 652d1bef4edSIngo Molnar idesc->chip = irq_type; 6531da177e4SLinus Torvalds } 65414454a1bSKenji Kaneshige return 0; 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds static unsigned int 6584bbdec7aSYasuaki Ishimatsu get_target_cpu (unsigned int gsi, int irq) 6591da177e4SLinus Torvalds { 6601da177e4SLinus Torvalds #ifdef CONFIG_SMP 6611da177e4SLinus Torvalds static int cpu = -1; 662ff741906SAshok Raj extern int cpe_vector; 6634994be1bSYasuaki Ishimatsu cpumask_t domain = irq_to_domain(irq); 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds /* 66624eeb568SKenji Kaneshige * In case of vector shared by multiple RTEs, all RTEs that 66724eeb568SKenji Kaneshige * share the vector need to use the same destination CPU. 66824eeb568SKenji Kaneshige */ 669c4c376f7SKenji Kaneshige if (iosapic_intr_info[irq].count) 6704bbdec7aSYasuaki Ishimatsu return iosapic_intr_info[irq].dest; 67124eeb568SKenji Kaneshige 67224eeb568SKenji Kaneshige /* 6731da177e4SLinus Torvalds * If the platform supports redirection via XTP, let it 6741da177e4SLinus Torvalds * distribute interrupts. 6751da177e4SLinus Torvalds */ 6761da177e4SLinus Torvalds if (smp_int_redirect & SMP_IRQ_REDIRECTION) 6771da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds /* 6801da177e4SLinus Torvalds * Some interrupts (ACPI SCI, for instance) are registered 6811da177e4SLinus Torvalds * before the BSP is marked as online. 6821da177e4SLinus Torvalds */ 6831da177e4SLinus Torvalds if (!cpu_online(smp_processor_id())) 6841da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6851da177e4SLinus Torvalds 686ff741906SAshok Raj #ifdef CONFIG_ACPI 6874bbdec7aSYasuaki Ishimatsu if (cpe_vector > 0 && irq_to_vector(irq) == IA64_CPEP_VECTOR) 688ff741906SAshok Raj return get_cpei_target_cpu(); 689ff741906SAshok Raj #endif 690ff741906SAshok Raj 6911da177e4SLinus Torvalds #ifdef CONFIG_NUMA 6921da177e4SLinus Torvalds { 6931da177e4SLinus Torvalds int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0; 6941da177e4SLinus Torvalds cpumask_t cpu_mask; 6951da177e4SLinus Torvalds 6961da177e4SLinus Torvalds iosapic_index = find_iosapic(gsi); 6971da177e4SLinus Torvalds if (iosapic_index < 0 || 6981da177e4SLinus Torvalds iosapic_lists[iosapic_index].node == MAX_NUMNODES) 6991da177e4SLinus Torvalds goto skip_numa_setup; 7001da177e4SLinus Torvalds 7011da177e4SLinus Torvalds cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node); 7024994be1bSYasuaki Ishimatsu cpus_and(cpu_mask, cpu_mask, domain); 7031da177e4SLinus Torvalds for_each_cpu_mask(numa_cpu, cpu_mask) { 7041da177e4SLinus Torvalds if (!cpu_online(numa_cpu)) 7051da177e4SLinus Torvalds cpu_clear(numa_cpu, cpu_mask); 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds num_cpus = cpus_weight(cpu_mask); 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds if (!num_cpus) 7111da177e4SLinus Torvalds goto skip_numa_setup; 7121da177e4SLinus Torvalds 7134bbdec7aSYasuaki Ishimatsu /* Use irq assignment to distribute across cpus in node */ 7144bbdec7aSYasuaki Ishimatsu cpu_index = irq % num_cpus; 7151da177e4SLinus Torvalds 7161da177e4SLinus Torvalds for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++) 7171da177e4SLinus Torvalds numa_cpu = next_cpu(numa_cpu, cpu_mask); 7181da177e4SLinus Torvalds 7191da177e4SLinus Torvalds if (numa_cpu != NR_CPUS) 7201da177e4SLinus Torvalds return cpu_physical_id(numa_cpu); 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds skip_numa_setup: 7231da177e4SLinus Torvalds #endif 7241da177e4SLinus Torvalds /* 7251da177e4SLinus Torvalds * Otherwise, round-robin interrupt vectors across all the 7261da177e4SLinus Torvalds * processors. (It'd be nice if we could be smarter in the 7271da177e4SLinus Torvalds * case of NUMA.) 7281da177e4SLinus Torvalds */ 7291da177e4SLinus Torvalds do { 7301da177e4SLinus Torvalds if (++cpu >= NR_CPUS) 7311da177e4SLinus Torvalds cpu = 0; 7324994be1bSYasuaki Ishimatsu } while (!cpu_online(cpu) || !cpu_isset(cpu, domain)); 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds return cpu_physical_id(cpu); 73546cba3dcSSatoru Takeuchi #else /* CONFIG_SMP */ 7361da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 7371da177e4SLinus Torvalds #endif 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 740c9d059deSKenji Kaneshige static inline unsigned char choose_dmode(void) 741c9d059deSKenji Kaneshige { 742c9d059deSKenji Kaneshige #ifdef CONFIG_SMP 743c9d059deSKenji Kaneshige if (smp_int_redirect & SMP_IRQ_REDIRECTION) 744c9d059deSKenji Kaneshige return IOSAPIC_LOWEST_PRIORITY; 745c9d059deSKenji Kaneshige #endif 746c9d059deSKenji Kaneshige return IOSAPIC_FIXED; 747c9d059deSKenji Kaneshige } 748c9d059deSKenji Kaneshige 7491da177e4SLinus Torvalds /* 7501da177e4SLinus Torvalds * ACPI can describe IOSAPIC interrupts via static tables and namespace 7511da177e4SLinus Torvalds * methods. This provides an interface to register those interrupts and 7521da177e4SLinus Torvalds * program the IOSAPIC RTE. 7531da177e4SLinus Torvalds */ 7541da177e4SLinus Torvalds int 7551da177e4SLinus Torvalds iosapic_register_intr (unsigned int gsi, 7561da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 7571da177e4SLinus Torvalds { 7584bbdec7aSYasuaki Ishimatsu int irq, mask = 1, err; 7591da177e4SLinus Torvalds unsigned int dest; 7601da177e4SLinus Torvalds unsigned long flags; 76124eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 76224eeb568SKenji Kaneshige u32 low32; 763c9d059deSKenji Kaneshige unsigned char dmode; 76440598cbeSYasuaki Ishimatsu 7651da177e4SLinus Torvalds /* 7661da177e4SLinus Torvalds * If this GSI has already been registered (i.e., it's a 7671da177e4SLinus Torvalds * shared interrupt, or we lost a race to register it), 7681da177e4SLinus Torvalds * don't touch the RTE. 7691da177e4SLinus Torvalds */ 7701da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 7714bbdec7aSYasuaki Ishimatsu irq = __gsi_to_irq(gsi); 7724bbdec7aSYasuaki Ishimatsu if (irq > 0) { 7734bbdec7aSYasuaki Ishimatsu rte = find_rte(irq, gsi); 774e1b30a39SYasuaki Ishimatsu if(iosapic_intr_info[irq].count == 0) { 775e1b30a39SYasuaki Ishimatsu assign_irq_vector(irq); 776e1b30a39SYasuaki Ishimatsu dynamic_irq_init(irq); 777e1b30a39SYasuaki Ishimatsu } else if (rte->refcnt != NO_REF_RTE) { 77824eeb568SKenji Kaneshige rte->refcnt++; 77940598cbeSYasuaki Ishimatsu goto unlock_iosapic_lock; 7801da177e4SLinus Torvalds } 781e1b30a39SYasuaki Ishimatsu } else 782e1b30a39SYasuaki Ishimatsu irq = create_irq(); 7831da177e4SLinus Torvalds 78424eeb568SKenji Kaneshige /* If vector is running out, we try to find a sharable vector */ 785eb21ab24SYasuaki Ishimatsu if (irq < 0) { 7864bbdec7aSYasuaki Ishimatsu irq = iosapic_find_sharable_irq(trigger, polarity); 7874bbdec7aSYasuaki Ishimatsu if (irq < 0) 78840598cbeSYasuaki Ishimatsu goto unlock_iosapic_lock; 7894bbdec7aSYasuaki Ishimatsu } 79024eeb568SKenji Kaneshige 7914bbdec7aSYasuaki Ishimatsu spin_lock(&irq_desc[irq].lock); 7924bbdec7aSYasuaki Ishimatsu dest = get_target_cpu(gsi, irq); 793c9d059deSKenji Kaneshige dmode = choose_dmode(); 794c9d059deSKenji Kaneshige err = register_intr(gsi, irq, dmode, polarity, trigger); 79514454a1bSKenji Kaneshige if (err < 0) { 796224685c0SKenji Kaneshige spin_unlock(&irq_desc[irq].lock); 7974bbdec7aSYasuaki Ishimatsu irq = err; 798224685c0SKenji Kaneshige goto unlock_iosapic_lock; 79914454a1bSKenji Kaneshige } 8001da177e4SLinus Torvalds 80124eeb568SKenji Kaneshige /* 802e3a8f7b8SYasuaki Ishimatsu * If the vector is shared and already unmasked for other 803e3a8f7b8SYasuaki Ishimatsu * interrupt sources, don't mask it. 80424eeb568SKenji Kaneshige */ 8054bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32; 8064bbdec7aSYasuaki Ishimatsu if (irq_is_shared(irq) && !(low32 & IOSAPIC_MASK)) 80724eeb568SKenji Kaneshige mask = 0; 8084bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, mask); 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", 8111da177e4SLinus Torvalds gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 8121da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 8134bbdec7aSYasuaki Ishimatsu cpu_logical_id(dest), dest, irq_to_vector(irq)); 814224685c0SKenji Kaneshige 8154bbdec7aSYasuaki Ishimatsu spin_unlock(&irq_desc[irq].lock); 81640598cbeSYasuaki Ishimatsu unlock_iosapic_lock: 81740598cbeSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 8184bbdec7aSYasuaki Ishimatsu return irq; 8191da177e4SLinus Torvalds } 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds void 8221da177e4SLinus Torvalds iosapic_unregister_intr (unsigned int gsi) 8231da177e4SLinus Torvalds { 8241da177e4SLinus Torvalds unsigned long flags; 8254bbdec7aSYasuaki Ishimatsu int irq, index; 8261da177e4SLinus Torvalds irq_desc_t *idesc; 82724eeb568SKenji Kaneshige u32 low32; 8281da177e4SLinus Torvalds unsigned long trigger, polarity; 82924eeb568SKenji Kaneshige unsigned int dest; 83024eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 8311da177e4SLinus Torvalds 8321da177e4SLinus Torvalds /* 8331da177e4SLinus Torvalds * If the irq associated with the gsi is not found, 8341da177e4SLinus Torvalds * iosapic_unregister_intr() is unbalanced. We need to check 8351da177e4SLinus Torvalds * this again after getting locks. 8361da177e4SLinus Torvalds */ 8371da177e4SLinus Torvalds irq = gsi_to_irq(gsi); 8381da177e4SLinus Torvalds if (irq < 0) { 83946cba3dcSSatoru Takeuchi printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", 84046cba3dcSSatoru Takeuchi gsi); 8411da177e4SLinus Torvalds WARN_ON(1); 8421da177e4SLinus Torvalds return; 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds 84540598cbeSYasuaki Ishimatsu spin_lock_irqsave(&iosapic_lock, flags); 8464bbdec7aSYasuaki Ishimatsu if ((rte = find_rte(irq, gsi)) == NULL) { 847e3a8f7b8SYasuaki Ishimatsu printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", 84846cba3dcSSatoru Takeuchi gsi); 8491da177e4SLinus Torvalds WARN_ON(1); 85024eeb568SKenji Kaneshige goto out; 8511da177e4SLinus Torvalds } 8521da177e4SLinus Torvalds 85324eeb568SKenji Kaneshige if (--rte->refcnt > 0) 85424eeb568SKenji Kaneshige goto out; 8551da177e4SLinus Torvalds 85640598cbeSYasuaki Ishimatsu idesc = irq_desc + irq; 857e1b30a39SYasuaki Ishimatsu rte->refcnt = NO_REF_RTE; 85840598cbeSYasuaki Ishimatsu 85924eeb568SKenji Kaneshige /* Mask the interrupt */ 8604bbdec7aSYasuaki Ishimatsu low32 = iosapic_intr_info[irq].low32 | IOSAPIC_MASK; 861c1726d6fSYasuaki Ishimatsu iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte->rte_index), low32); 8621da177e4SLinus Torvalds 8634bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].count--; 8640e888adcSKenji Kaneshige index = find_iosapic(gsi); 8650e888adcSKenji Kaneshige iosapic_lists[index].rtes_inuse--; 8660e888adcSKenji Kaneshige WARN_ON(iosapic_lists[index].rtes_inuse < 0); 8671da177e4SLinus Torvalds 8684bbdec7aSYasuaki Ishimatsu trigger = iosapic_intr_info[irq].trigger; 8694bbdec7aSYasuaki Ishimatsu polarity = iosapic_intr_info[irq].polarity; 8704bbdec7aSYasuaki Ishimatsu dest = iosapic_intr_info[irq].dest; 87146cba3dcSSatoru Takeuchi printk(KERN_INFO 872e3a8f7b8SYasuaki Ishimatsu "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n", 87324eeb568SKenji Kaneshige gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 87424eeb568SKenji Kaneshige (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 8754bbdec7aSYasuaki Ishimatsu cpu_logical_id(dest), dest, irq_to_vector(irq)); 8761da177e4SLinus Torvalds 877e1b30a39SYasuaki Ishimatsu if (iosapic_intr_info[irq].count == 0) { 878451fe00cSAlex Williamson #ifdef CONFIG_SMP 879451fe00cSAlex Williamson /* Clear affinity */ 880451fe00cSAlex Williamson cpus_setall(idesc->affinity); 881451fe00cSAlex Williamson #endif 88224eeb568SKenji Kaneshige /* Clear the interrupt information */ 883e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].dest = 0; 884e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].dmode = 0; 885e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].polarity = 0; 886e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].trigger = 0; 8874bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 |= IOSAPIC_MASK; 88824eeb568SKenji Kaneshige 889e1b30a39SYasuaki Ishimatsu /* Destroy and reserve IRQ */ 890e1b30a39SYasuaki Ishimatsu destroy_and_reserve_irq(irq); 89124eeb568SKenji Kaneshige } 89224eeb568SKenji Kaneshige out: 89340598cbeSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds /* 8971da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a platform interrupt. 8981da177e4SLinus Torvalds */ 8991da177e4SLinus Torvalds int __init 9001da177e4SLinus Torvalds iosapic_register_platform_intr (u32 int_type, unsigned int gsi, 9011da177e4SLinus Torvalds int iosapic_vector, u16 eid, u16 id, 9021da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 9031da177e4SLinus Torvalds { 9041da177e4SLinus Torvalds static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"}; 9051da177e4SLinus Torvalds unsigned char delivery; 906eb21ab24SYasuaki Ishimatsu int irq, vector, mask = 0; 9071da177e4SLinus Torvalds unsigned int dest = ((id << 8) | eid) & 0xffff; 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds switch (int_type) { 9101da177e4SLinus Torvalds case ACPI_INTERRUPT_PMI: 911e1b30a39SYasuaki Ishimatsu irq = vector = iosapic_vector; 9124994be1bSYasuaki Ishimatsu bind_irq_vector(irq, vector, CPU_MASK_ALL); 9131da177e4SLinus Torvalds /* 9141da177e4SLinus Torvalds * since PMI vector is alloc'd by FW(ACPI) not by kernel, 9151da177e4SLinus Torvalds * we need to make sure the vector is available 9161da177e4SLinus Torvalds */ 9174bbdec7aSYasuaki Ishimatsu iosapic_reassign_vector(irq); 9181da177e4SLinus Torvalds delivery = IOSAPIC_PMI; 9191da177e4SLinus Torvalds break; 9201da177e4SLinus Torvalds case ACPI_INTERRUPT_INIT: 921eb21ab24SYasuaki Ishimatsu irq = create_irq(); 922eb21ab24SYasuaki Ishimatsu if (irq < 0) 9233b5cc090SKenji Kaneshige panic("%s: out of interrupt vectors!\n", __FUNCTION__); 924eb21ab24SYasuaki Ishimatsu vector = irq_to_vector(irq); 9251da177e4SLinus Torvalds delivery = IOSAPIC_INIT; 9261da177e4SLinus Torvalds break; 9271da177e4SLinus Torvalds case ACPI_INTERRUPT_CPEI: 928e1b30a39SYasuaki Ishimatsu irq = vector = IA64_CPE_VECTOR; 9294994be1bSYasuaki Ishimatsu BUG_ON(bind_irq_vector(irq, vector, CPU_MASK_ALL)); 930aa0ebec9SKenji Kaneshige delivery = IOSAPIC_FIXED; 9311da177e4SLinus Torvalds mask = 1; 9321da177e4SLinus Torvalds break; 9331da177e4SLinus Torvalds default: 93446cba3dcSSatoru Takeuchi printk(KERN_ERR "%s: invalid int type 0x%x\n", __FUNCTION__, 93546cba3dcSSatoru Takeuchi int_type); 9361da177e4SLinus Torvalds return -1; 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds 9394bbdec7aSYasuaki Ishimatsu register_intr(gsi, irq, delivery, polarity, trigger); 9401da177e4SLinus Torvalds 94146cba3dcSSatoru Takeuchi printk(KERN_INFO 94246cba3dcSSatoru Takeuchi "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x)" 94346cba3dcSSatoru Takeuchi " vector %d\n", 9441da177e4SLinus Torvalds int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown", 9451da177e4SLinus Torvalds int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 9461da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 9471da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9481da177e4SLinus Torvalds 9494bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, mask); 9501da177e4SLinus Torvalds return vector; 9511da177e4SLinus Torvalds } 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds /* 9541da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a legacy ISA IRQ override. 9551da177e4SLinus Torvalds */ 9560f7ac29eSTony Luck void __devinit 9571da177e4SLinus Torvalds iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, 9581da177e4SLinus Torvalds unsigned long polarity, 9591da177e4SLinus Torvalds unsigned long trigger) 9601da177e4SLinus Torvalds { 9614bbdec7aSYasuaki Ishimatsu int vector, irq; 9621da177e4SLinus Torvalds unsigned int dest = cpu_physical_id(smp_processor_id()); 963c9d059deSKenji Kaneshige unsigned char dmode; 9641da177e4SLinus Torvalds 965e1b30a39SYasuaki Ishimatsu irq = vector = isa_irq_to_vector(isa_irq); 9664994be1bSYasuaki Ishimatsu BUG_ON(bind_irq_vector(irq, vector, CPU_MASK_ALL)); 967c9d059deSKenji Kaneshige dmode = choose_dmode(); 968c9d059deSKenji Kaneshige register_intr(gsi, irq, dmode, polarity, trigger); 9691da177e4SLinus Torvalds 9701da177e4SLinus Torvalds DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n", 9711da177e4SLinus Torvalds isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level", 9721da177e4SLinus Torvalds polarity == IOSAPIC_POL_HIGH ? "high" : "low", 9731da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9741da177e4SLinus Torvalds 9754bbdec7aSYasuaki Ishimatsu set_rte(gsi, irq, dest, 1); 9761da177e4SLinus Torvalds } 9771da177e4SLinus Torvalds 9781da177e4SLinus Torvalds void __init 9791da177e4SLinus Torvalds iosapic_system_init (int system_pcat_compat) 9801da177e4SLinus Torvalds { 9814bbdec7aSYasuaki Ishimatsu int irq; 9821da177e4SLinus Torvalds 9834bbdec7aSYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; ++irq) { 9844bbdec7aSYasuaki Ishimatsu iosapic_intr_info[irq].low32 = IOSAPIC_MASK; 98546cba3dcSSatoru Takeuchi /* mark as unused */ 9864bbdec7aSYasuaki Ishimatsu INIT_LIST_HEAD(&iosapic_intr_info[irq].rtes); 987e1b30a39SYasuaki Ishimatsu 988e1b30a39SYasuaki Ishimatsu iosapic_intr_info[irq].count = 0; 98924eeb568SKenji Kaneshige } 9901da177e4SLinus Torvalds 9911da177e4SLinus Torvalds pcat_compat = system_pcat_compat; 9921da177e4SLinus Torvalds if (pcat_compat) { 9931da177e4SLinus Torvalds /* 99446cba3dcSSatoru Takeuchi * Disable the compatibility mode interrupts (8259 style), 99546cba3dcSSatoru Takeuchi * needs IN/OUT support enabled. 9961da177e4SLinus Torvalds */ 99746cba3dcSSatoru Takeuchi printk(KERN_INFO 99846cba3dcSSatoru Takeuchi "%s: Disabling PC-AT compatible 8259 interrupts\n", 99946cba3dcSSatoru Takeuchi __FUNCTION__); 10001da177e4SLinus Torvalds outb(0xff, 0xA1); 10011da177e4SLinus Torvalds outb(0xff, 0x21); 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds 10050e888adcSKenji Kaneshige static inline int 10060e888adcSKenji Kaneshige iosapic_alloc (void) 10070e888adcSKenji Kaneshige { 10080e888adcSKenji Kaneshige int index; 10090e888adcSKenji Kaneshige 10100e888adcSKenji Kaneshige for (index = 0; index < NR_IOSAPICS; index++) 10110e888adcSKenji Kaneshige if (!iosapic_lists[index].addr) 10120e888adcSKenji Kaneshige return index; 10130e888adcSKenji Kaneshige 10140e888adcSKenji Kaneshige printk(KERN_WARNING "%s: failed to allocate iosapic\n", __FUNCTION__); 10150e888adcSKenji Kaneshige return -1; 10160e888adcSKenji Kaneshige } 10170e888adcSKenji Kaneshige 10180e888adcSKenji Kaneshige static inline void 10190e888adcSKenji Kaneshige iosapic_free (int index) 10200e888adcSKenji Kaneshige { 10210e888adcSKenji Kaneshige memset(&iosapic_lists[index], 0, sizeof(iosapic_lists[0])); 10220e888adcSKenji Kaneshige } 10230e888adcSKenji Kaneshige 10240e888adcSKenji Kaneshige static inline int 10250e888adcSKenji Kaneshige iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver) 10260e888adcSKenji Kaneshige { 10270e888adcSKenji Kaneshige int index; 10280e888adcSKenji Kaneshige unsigned int gsi_end, base, end; 10290e888adcSKenji Kaneshige 10300e888adcSKenji Kaneshige /* check gsi range */ 10310e888adcSKenji Kaneshige gsi_end = gsi_base + ((ver >> 16) & 0xff); 10320e888adcSKenji Kaneshige for (index = 0; index < NR_IOSAPICS; index++) { 10330e888adcSKenji Kaneshige if (!iosapic_lists[index].addr) 10340e888adcSKenji Kaneshige continue; 10350e888adcSKenji Kaneshige 10360e888adcSKenji Kaneshige base = iosapic_lists[index].gsi_base; 10370e888adcSKenji Kaneshige end = base + iosapic_lists[index].num_rte - 1; 10380e888adcSKenji Kaneshige 1039e6d1ba5cSSatoru Takeuchi if (gsi_end < base || end < gsi_base) 10400e888adcSKenji Kaneshige continue; /* OK */ 10410e888adcSKenji Kaneshige 10420e888adcSKenji Kaneshige return -EBUSY; 10430e888adcSKenji Kaneshige } 10440e888adcSKenji Kaneshige return 0; 10450e888adcSKenji Kaneshige } 10460e888adcSKenji Kaneshige 10470e888adcSKenji Kaneshige int __devinit 10481da177e4SLinus Torvalds iosapic_init (unsigned long phys_addr, unsigned int gsi_base) 10491da177e4SLinus Torvalds { 10500e888adcSKenji Kaneshige int num_rte, err, index; 10511da177e4SLinus Torvalds unsigned int isa_irq, ver; 10521da177e4SLinus Torvalds char __iomem *addr; 10530e888adcSKenji Kaneshige unsigned long flags; 10541da177e4SLinus Torvalds 10550e888adcSKenji Kaneshige spin_lock_irqsave(&iosapic_lock, flags); 1056c1726d6fSYasuaki Ishimatsu index = find_iosapic(gsi_base); 1057c1726d6fSYasuaki Ishimatsu if (index >= 0) { 1058c1726d6fSYasuaki Ishimatsu spin_unlock_irqrestore(&iosapic_lock, flags); 1059c1726d6fSYasuaki Ishimatsu return -EBUSY; 1060c1726d6fSYasuaki Ishimatsu } 1061c1726d6fSYasuaki Ishimatsu 10621da177e4SLinus Torvalds addr = ioremap(phys_addr, 0); 10631da177e4SLinus Torvalds ver = iosapic_version(addr); 10640e888adcSKenji Kaneshige if ((err = iosapic_check_gsi_range(gsi_base, ver))) { 10650e888adcSKenji Kaneshige iounmap(addr); 10660e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 10670e888adcSKenji Kaneshige return err; 10680e888adcSKenji Kaneshige } 10690e888adcSKenji Kaneshige 10701da177e4SLinus Torvalds /* 1071e3a8f7b8SYasuaki Ishimatsu * The MAX_REDIR register holds the highest input pin number 1072e3a8f7b8SYasuaki Ishimatsu * (starting from 0). We add 1 so that we can use it for 1073e3a8f7b8SYasuaki Ishimatsu * number of pins (= RTEs) 10741da177e4SLinus Torvalds */ 10751da177e4SLinus Torvalds num_rte = ((ver >> 16) & 0xff) + 1; 10761da177e4SLinus Torvalds 10770e888adcSKenji Kaneshige index = iosapic_alloc(); 10780e888adcSKenji Kaneshige iosapic_lists[index].addr = addr; 10790e888adcSKenji Kaneshige iosapic_lists[index].gsi_base = gsi_base; 10800e888adcSKenji Kaneshige iosapic_lists[index].num_rte = num_rte; 10811da177e4SLinus Torvalds #ifdef CONFIG_NUMA 10820e888adcSKenji Kaneshige iosapic_lists[index].node = MAX_NUMNODES; 10831da177e4SLinus Torvalds #endif 1084c1726d6fSYasuaki Ishimatsu spin_lock_init(&iosapic_lists[index].lock); 10850e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds if ((gsi_base == 0) && pcat_compat) { 10881da177e4SLinus Torvalds /* 108946cba3dcSSatoru Takeuchi * Map the legacy ISA devices into the IOSAPIC data. Some of 109046cba3dcSSatoru Takeuchi * these may get reprogrammed later on with data from the ACPI 109146cba3dcSSatoru Takeuchi * Interrupt Source Override table. 10921da177e4SLinus Torvalds */ 10931da177e4SLinus Torvalds for (isa_irq = 0; isa_irq < 16; ++isa_irq) 109446cba3dcSSatoru Takeuchi iosapic_override_isa_irq(isa_irq, isa_irq, 109546cba3dcSSatoru Takeuchi IOSAPIC_POL_HIGH, 109646cba3dcSSatoru Takeuchi IOSAPIC_EDGE); 10971da177e4SLinus Torvalds } 10980e888adcSKenji Kaneshige return 0; 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds 11010e888adcSKenji Kaneshige #ifdef CONFIG_HOTPLUG 11020e888adcSKenji Kaneshige int 11030e888adcSKenji Kaneshige iosapic_remove (unsigned int gsi_base) 11040e888adcSKenji Kaneshige { 11050e888adcSKenji Kaneshige int index, err = 0; 11060e888adcSKenji Kaneshige unsigned long flags; 11070e888adcSKenji Kaneshige 11080e888adcSKenji Kaneshige spin_lock_irqsave(&iosapic_lock, flags); 11090e888adcSKenji Kaneshige index = find_iosapic(gsi_base); 11100e888adcSKenji Kaneshige if (index < 0) { 11110e888adcSKenji Kaneshige printk(KERN_WARNING "%s: No IOSAPIC for GSI base %u\n", 11120e888adcSKenji Kaneshige __FUNCTION__, gsi_base); 11130e888adcSKenji Kaneshige goto out; 11140e888adcSKenji Kaneshige } 11150e888adcSKenji Kaneshige 11160e888adcSKenji Kaneshige if (iosapic_lists[index].rtes_inuse) { 11170e888adcSKenji Kaneshige err = -EBUSY; 1118e3a8f7b8SYasuaki Ishimatsu printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n", 11190e888adcSKenji Kaneshige __FUNCTION__, gsi_base); 11200e888adcSKenji Kaneshige goto out; 11210e888adcSKenji Kaneshige } 11220e888adcSKenji Kaneshige 11230e888adcSKenji Kaneshige iounmap(iosapic_lists[index].addr); 11240e888adcSKenji Kaneshige iosapic_free(index); 11250e888adcSKenji Kaneshige out: 11260e888adcSKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 11270e888adcSKenji Kaneshige return err; 11280e888adcSKenji Kaneshige } 11290e888adcSKenji Kaneshige #endif /* CONFIG_HOTPLUG */ 11300e888adcSKenji Kaneshige 11311da177e4SLinus Torvalds #ifdef CONFIG_NUMA 11320e888adcSKenji Kaneshige void __devinit 11331da177e4SLinus Torvalds map_iosapic_to_node(unsigned int gsi_base, int node) 11341da177e4SLinus Torvalds { 11351da177e4SLinus Torvalds int index; 11361da177e4SLinus Torvalds 11371da177e4SLinus Torvalds index = find_iosapic(gsi_base); 11381da177e4SLinus Torvalds if (index < 0) { 11391da177e4SLinus Torvalds printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", 11401da177e4SLinus Torvalds __FUNCTION__, gsi_base); 11411da177e4SLinus Torvalds return; 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds iosapic_lists[index].node = node; 11441da177e4SLinus Torvalds return; 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds #endif 114724eeb568SKenji Kaneshige 114824eeb568SKenji Kaneshige static int __init iosapic_enable_kmalloc (void) 114924eeb568SKenji Kaneshige { 115024eeb568SKenji Kaneshige iosapic_kmalloc_ok = 1; 115124eeb568SKenji Kaneshige return 0; 115224eeb568SKenji Kaneshige } 115324eeb568SKenji Kaneshige core_initcall (iosapic_enable_kmalloc); 1154