11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * I/O SAPIC support. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1999 Intel Corp. 51da177e4SLinus Torvalds * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com> 61da177e4SLinus Torvalds * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com> 71da177e4SLinus Torvalds * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co. 81da177e4SLinus Torvalds * David Mosberger-Tang <davidm@hpl.hp.com> 91da177e4SLinus Torvalds * Copyright (C) 1999 VA Linux Systems 101da177e4SLinus Torvalds * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * 00/04/19 D. Mosberger Rewritten to mirror more closely the x86 I/O APIC code. 131da177e4SLinus Torvalds * In particular, we now have separate handlers for edge 141da177e4SLinus Torvalds * and level triggered interrupts. 151da177e4SLinus Torvalds * 00/10/27 Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation 161da177e4SLinus Torvalds * PCI to vector mapping, shared PCI interrupts. 171da177e4SLinus Torvalds * 00/10/27 D. Mosberger Document things a bit more to make them more understandable. 181da177e4SLinus Torvalds * Clean up much of the old IOSAPIC cruft. 191da177e4SLinus Torvalds * 01/07/27 J.I. Lee PCI irq routing, Platform/Legacy interrupts and fixes for 201da177e4SLinus Torvalds * ACPI S5(SoftOff) support. 211da177e4SLinus Torvalds * 02/01/23 J.I. Lee iosapic pgm fixes for PCI irq routing from _PRT 221da177e4SLinus Torvalds * 02/01/07 E. Focht <efocht@ess.nec.de> Redirectable interrupt vectors in 231da177e4SLinus Torvalds * iosapic_set_affinity(), initializations for 241da177e4SLinus Torvalds * /proc/irq/#/smp_affinity 251da177e4SLinus Torvalds * 02/04/02 P. Diefenbaugh Cleaned up ACPI PCI IRQ routing. 261da177e4SLinus Torvalds * 02/04/18 J.I. Lee bug fix in iosapic_init_pci_irq 271da177e4SLinus Torvalds * 02/04/30 J.I. Lee bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping 281da177e4SLinus Torvalds * error 291da177e4SLinus Torvalds * 02/07/29 T. Kochi Allocate interrupt vectors dynamically 301da177e4SLinus Torvalds * 02/08/04 T. Kochi Cleaned up terminology (irq, global system interrupt, vector, etc.) 311da177e4SLinus Torvalds * 02/09/20 D. Mosberger Simplified by taking advantage of ACPI's pci_irq code. 321da177e4SLinus Torvalds * 03/02/19 B. Helgaas Make pcat_compat system-wide, not per-IOSAPIC. 331da177e4SLinus Torvalds * Remove iosapic_address & gsi_base from external interfaces. 341da177e4SLinus Torvalds * Rationalize __init/__devinit attributes. 351da177e4SLinus Torvalds * 04/12/04 Ashok Raj <ashok.raj@intel.com> Intel Corporation 2004 361da177e4SLinus Torvalds * Updated to work with irq migration necessary for CPU Hotplug 371da177e4SLinus Torvalds */ 381da177e4SLinus Torvalds /* 391da177e4SLinus Torvalds * Here is what the interrupt logic between a PCI device and the kernel looks like: 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD). The 421da177e4SLinus Torvalds * device is uniquely identified by its bus--, and slot-number (the function 431da177e4SLinus Torvalds * number does not matter here because all functions share the same interrupt 441da177e4SLinus Torvalds * lines). 451da177e4SLinus Torvalds * 461da177e4SLinus Torvalds * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller. 471da177e4SLinus Torvalds * Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level 481da177e4SLinus Torvalds * triggered and use the same polarity). Each interrupt line has a unique Global 491da177e4SLinus Torvalds * System Interrupt (GSI) number which can be calculated as the sum of the controller's 501da177e4SLinus Torvalds * base GSI number and the IOSAPIC pin number to which the line connects. 511da177e4SLinus Torvalds * 521da177e4SLinus Torvalds * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin 531da177e4SLinus Torvalds * into the IA-64 interrupt vector. This interrupt vector is then sent to the CPU. 541da177e4SLinus Torvalds * 551da177e4SLinus Torvalds * (4) The kernel recognizes an interrupt as an IRQ. The IRQ interface is used as 561da177e4SLinus Torvalds * architecture-independent interrupt handling mechanism in Linux. As an 571da177e4SLinus Torvalds * IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number 581da177e4SLinus Torvalds * mapping. On smaller systems, we use one-to-one mapping between IA-64 vector and 591da177e4SLinus Torvalds * IRQ. A platform can implement platform_irq_to_vector(irq) and 601da177e4SLinus Torvalds * platform_local_vector_to_irq(vector) APIs to differentiate the mapping. 611da177e4SLinus Torvalds * Please see also include/asm-ia64/hw_irq.h for those APIs. 621da177e4SLinus Torvalds * 631da177e4SLinus Torvalds * To sum up, there are three levels of mappings involved: 641da177e4SLinus Torvalds * 651da177e4SLinus Torvalds * PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ 661da177e4SLinus Torvalds * 671da177e4SLinus Torvalds * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts. 681da177e4SLinus Torvalds * Now we use "IRQ" only for Linux IRQ's. ISA IRQ (isa_irq) is the only exception in this 691da177e4SLinus Torvalds * source code. 701da177e4SLinus Torvalds */ 711da177e4SLinus Torvalds #include <linux/config.h> 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds #include <linux/acpi.h> 741da177e4SLinus Torvalds #include <linux/init.h> 751da177e4SLinus Torvalds #include <linux/irq.h> 761da177e4SLinus Torvalds #include <linux/kernel.h> 771da177e4SLinus Torvalds #include <linux/list.h> 781da177e4SLinus Torvalds #include <linux/pci.h> 791da177e4SLinus Torvalds #include <linux/smp.h> 801da177e4SLinus Torvalds #include <linux/smp_lock.h> 811da177e4SLinus Torvalds #include <linux/string.h> 8224eeb568SKenji Kaneshige #include <linux/bootmem.h> 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds #include <asm/delay.h> 851da177e4SLinus Torvalds #include <asm/hw_irq.h> 861da177e4SLinus Torvalds #include <asm/io.h> 871da177e4SLinus Torvalds #include <asm/iosapic.h> 881da177e4SLinus Torvalds #include <asm/machvec.h> 891da177e4SLinus Torvalds #include <asm/processor.h> 901da177e4SLinus Torvalds #include <asm/ptrace.h> 911da177e4SLinus Torvalds #include <asm/system.h> 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds #undef DEBUG_INTERRUPT_ROUTING 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds #ifdef DEBUG_INTERRUPT_ROUTING 971da177e4SLinus Torvalds #define DBG(fmt...) printk(fmt) 981da177e4SLinus Torvalds #else 991da177e4SLinus Torvalds #define DBG(fmt...) 1001da177e4SLinus Torvalds #endif 1011da177e4SLinus Torvalds 10224eeb568SKenji Kaneshige #define NR_PREALLOCATE_RTE_ENTRIES (PAGE_SIZE / sizeof(struct iosapic_rte_info)) 10324eeb568SKenji Kaneshige #define RTE_PREALLOCATED (1) 10424eeb568SKenji Kaneshige 1051da177e4SLinus Torvalds static DEFINE_SPINLOCK(iosapic_lock); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ 1081da177e4SLinus Torvalds 10924eeb568SKenji Kaneshige struct iosapic_rte_info { 11024eeb568SKenji Kaneshige struct list_head rte_list; /* node in list of RTEs sharing the same vector */ 1111da177e4SLinus Torvalds char __iomem *addr; /* base address of IOSAPIC */ 1121da177e4SLinus Torvalds unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ 11324eeb568SKenji Kaneshige char rte_index; /* IOSAPIC RTE index */ 11424eeb568SKenji Kaneshige int refcnt; /* reference counter */ 11524eeb568SKenji Kaneshige unsigned int flags; /* flags */ 11624eeb568SKenji Kaneshige } ____cacheline_aligned; 11724eeb568SKenji Kaneshige 11824eeb568SKenji Kaneshige static struct iosapic_intr_info { 11924eeb568SKenji Kaneshige struct list_head rtes; /* RTEs using this vector (empty => not an IOSAPIC interrupt) */ 12024eeb568SKenji Kaneshige int count; /* # of RTEs that shares this vector */ 12124eeb568SKenji Kaneshige u32 low32; /* current value of low word of Redirection table entry */ 12224eeb568SKenji Kaneshige unsigned int dest; /* destination CPU physical ID */ 1231da177e4SLinus Torvalds unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ 1241da177e4SLinus Torvalds unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */ 1251da177e4SLinus Torvalds unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ 1261da177e4SLinus Torvalds } iosapic_intr_info[IA64_NUM_VECTORS]; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds static struct iosapic { 1291da177e4SLinus Torvalds char __iomem *addr; /* base address of IOSAPIC */ 1301da177e4SLinus Torvalds unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ 1311da177e4SLinus Torvalds unsigned short num_rte; /* number of RTE in this IOSAPIC */ 1321da177e4SLinus Torvalds #ifdef CONFIG_NUMA 1331da177e4SLinus Torvalds unsigned short node; /* numa node association via pxm */ 1341da177e4SLinus Torvalds #endif 1351da177e4SLinus Torvalds } iosapic_lists[NR_IOSAPICS]; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds static int num_iosapic; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds static unsigned char pcat_compat __initdata; /* 8259 compatibility flag */ 1401da177e4SLinus Torvalds 14124eeb568SKenji Kaneshige static int iosapic_kmalloc_ok; 14224eeb568SKenji Kaneshige static LIST_HEAD(free_rte_list); 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds /* 1451da177e4SLinus Torvalds * Find an IOSAPIC associated with a GSI 1461da177e4SLinus Torvalds */ 1471da177e4SLinus Torvalds static inline int 1481da177e4SLinus Torvalds find_iosapic (unsigned int gsi) 1491da177e4SLinus Torvalds { 1501da177e4SLinus Torvalds int i; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds for (i = 0; i < num_iosapic; i++) { 1531da177e4SLinus Torvalds if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte) 1541da177e4SLinus Torvalds return i; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds return -1; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds static inline int 1611da177e4SLinus Torvalds _gsi_to_vector (unsigned int gsi) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds struct iosapic_intr_info *info; 16424eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) 16724eeb568SKenji Kaneshige list_for_each_entry(rte, &info->rtes, rte_list) 16824eeb568SKenji Kaneshige if (rte->gsi_base + rte->rte_index == gsi) 1691da177e4SLinus Torvalds return info - iosapic_intr_info; 1701da177e4SLinus Torvalds return -1; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds /* 1741da177e4SLinus Torvalds * Translate GSI number to the corresponding IA-64 interrupt vector. If no 1751da177e4SLinus Torvalds * entry exists, return -1. 1761da177e4SLinus Torvalds */ 1771da177e4SLinus Torvalds inline int 1781da177e4SLinus Torvalds gsi_to_vector (unsigned int gsi) 1791da177e4SLinus Torvalds { 1801da177e4SLinus Torvalds return _gsi_to_vector(gsi); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds int 1841da177e4SLinus Torvalds gsi_to_irq (unsigned int gsi) 1851da177e4SLinus Torvalds { 18624eeb568SKenji Kaneshige unsigned long flags; 18724eeb568SKenji Kaneshige int irq; 1881da177e4SLinus Torvalds /* 1891da177e4SLinus Torvalds * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq 1901da177e4SLinus Torvalds * numbers... 1911da177e4SLinus Torvalds */ 19224eeb568SKenji Kaneshige spin_lock_irqsave(&iosapic_lock, flags); 19324eeb568SKenji Kaneshige { 19424eeb568SKenji Kaneshige irq = _gsi_to_vector(gsi); 19524eeb568SKenji Kaneshige } 19624eeb568SKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 19724eeb568SKenji Kaneshige 19824eeb568SKenji Kaneshige return irq; 19924eeb568SKenji Kaneshige } 20024eeb568SKenji Kaneshige 20124eeb568SKenji Kaneshige static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec) 20224eeb568SKenji Kaneshige { 20324eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 20424eeb568SKenji Kaneshige 20524eeb568SKenji Kaneshige list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) 20624eeb568SKenji Kaneshige if (rte->gsi_base + rte->rte_index == gsi) 20724eeb568SKenji Kaneshige return rte; 20824eeb568SKenji Kaneshige return NULL; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds static void 21224eeb568SKenji Kaneshige set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds unsigned long pol, trigger, dmode; 2151da177e4SLinus Torvalds u32 low32, high32; 2161da177e4SLinus Torvalds char __iomem *addr; 2171da177e4SLinus Torvalds int rte_index; 2181da177e4SLinus Torvalds char redir; 21924eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); 2221da177e4SLinus Torvalds 22324eeb568SKenji Kaneshige rte = gsi_vector_to_rte(gsi, vector); 22424eeb568SKenji Kaneshige if (!rte) 2251da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 2261da177e4SLinus Torvalds 22724eeb568SKenji Kaneshige rte_index = rte->rte_index; 22824eeb568SKenji Kaneshige addr = rte->addr; 2291da177e4SLinus Torvalds pol = iosapic_intr_info[vector].polarity; 2301da177e4SLinus Torvalds trigger = iosapic_intr_info[vector].trigger; 2311da177e4SLinus Torvalds dmode = iosapic_intr_info[vector].dmode; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds #ifdef CONFIG_SMP 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds unsigned int irq; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds for (irq = 0; irq < NR_IRQS; ++irq) 2401da177e4SLinus Torvalds if (irq_to_vector(irq) == vector) { 2411da177e4SLinus Torvalds set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); 2421da177e4SLinus Torvalds break; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds #endif 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds low32 = ((pol << IOSAPIC_POLARITY_SHIFT) | 2481da177e4SLinus Torvalds (trigger << IOSAPIC_TRIGGER_SHIFT) | 2491da177e4SLinus Torvalds (dmode << IOSAPIC_DELIVERY_SHIFT) | 2501da177e4SLinus Torvalds ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) | 2511da177e4SLinus Torvalds vector); 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds /* dest contains both id and eid */ 2541da177e4SLinus Torvalds high32 = (dest << IOSAPIC_DEST_SHIFT); 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); 2571da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); 2581da177e4SLinus Torvalds iosapic_intr_info[vector].low32 = low32; 25924eeb568SKenji Kaneshige iosapic_intr_info[vector].dest = dest; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds static void 2631da177e4SLinus Torvalds nop (unsigned int vector) 2641da177e4SLinus Torvalds { 2651da177e4SLinus Torvalds /* do nothing... */ 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds static void 2691da177e4SLinus Torvalds mask_irq (unsigned int irq) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds unsigned long flags; 2721da177e4SLinus Torvalds char __iomem *addr; 2731da177e4SLinus Torvalds u32 low32; 2741da177e4SLinus Torvalds int rte_index; 2751da177e4SLinus Torvalds ia64_vector vec = irq_to_vector(irq); 27624eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 2771da177e4SLinus Torvalds 27824eeb568SKenji Kaneshige if (list_empty(&iosapic_intr_info[vec].rtes)) 2791da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 2821da177e4SLinus Torvalds { 2831da177e4SLinus Torvalds /* set only the mask bit */ 2841da177e4SLinus Torvalds low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK; 28524eeb568SKenji Kaneshige list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { 28624eeb568SKenji Kaneshige addr = rte->addr; 28724eeb568SKenji Kaneshige rte_index = rte->rte_index; 2881da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); 2891da177e4SLinus Torvalds } 29024eeb568SKenji Kaneshige } 2911da177e4SLinus Torvalds spin_unlock_irqrestore(&iosapic_lock, flags); 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds static void 2951da177e4SLinus Torvalds unmask_irq (unsigned int irq) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds unsigned long flags; 2981da177e4SLinus Torvalds char __iomem *addr; 2991da177e4SLinus Torvalds u32 low32; 3001da177e4SLinus Torvalds int rte_index; 3011da177e4SLinus Torvalds ia64_vector vec = irq_to_vector(irq); 30224eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3031da177e4SLinus Torvalds 30424eeb568SKenji Kaneshige if (list_empty(&iosapic_intr_info[vec].rtes)) 3051da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt! */ 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK; 31024eeb568SKenji Kaneshige list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { 31124eeb568SKenji Kaneshige addr = rte->addr; 31224eeb568SKenji Kaneshige rte_index = rte->rte_index; 3131da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); 3141da177e4SLinus Torvalds } 31524eeb568SKenji Kaneshige } 3161da177e4SLinus Torvalds spin_unlock_irqrestore(&iosapic_lock, flags); 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds static void 3211da177e4SLinus Torvalds iosapic_set_affinity (unsigned int irq, cpumask_t mask) 3221da177e4SLinus Torvalds { 3231da177e4SLinus Torvalds #ifdef CONFIG_SMP 3241da177e4SLinus Torvalds unsigned long flags; 3251da177e4SLinus Torvalds u32 high32, low32; 3261da177e4SLinus Torvalds int dest, rte_index; 3271da177e4SLinus Torvalds char __iomem *addr; 3281da177e4SLinus Torvalds int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; 3291da177e4SLinus Torvalds ia64_vector vec; 33024eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds irq &= (~IA64_IRQ_REDIRECTED); 3331da177e4SLinus Torvalds vec = irq_to_vector(irq); 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds if (cpus_empty(mask)) 3361da177e4SLinus Torvalds return; 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds dest = cpu_physical_id(first_cpu(mask)); 3391da177e4SLinus Torvalds 34024eeb568SKenji Kaneshige if (list_empty(&iosapic_intr_info[vec].rtes)) 3411da177e4SLinus Torvalds return; /* not an IOSAPIC interrupt */ 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds set_irq_affinity_info(irq, dest, redir); 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds /* dest contains both id and eid */ 3461da177e4SLinus Torvalds high32 = dest << IOSAPIC_DEST_SHIFT; 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 3491da177e4SLinus Torvalds { 3501da177e4SLinus Torvalds low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds if (redir) 3531da177e4SLinus Torvalds /* change delivery mode to lowest priority */ 3541da177e4SLinus Torvalds low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); 3551da177e4SLinus Torvalds else 3561da177e4SLinus Torvalds /* change delivery mode to fixed */ 3571da177e4SLinus Torvalds low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds iosapic_intr_info[vec].low32 = low32; 36024eeb568SKenji Kaneshige iosapic_intr_info[vec].dest = dest; 36124eeb568SKenji Kaneshige list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { 36224eeb568SKenji Kaneshige addr = rte->addr; 36324eeb568SKenji Kaneshige rte_index = rte->rte_index; 3641da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); 3651da177e4SLinus Torvalds iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); 3661da177e4SLinus Torvalds } 36724eeb568SKenji Kaneshige } 3681da177e4SLinus Torvalds spin_unlock_irqrestore(&iosapic_lock, flags); 3691da177e4SLinus Torvalds #endif 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds /* 3731da177e4SLinus Torvalds * Handlers for level-triggered interrupts. 3741da177e4SLinus Torvalds */ 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds static unsigned int 3771da177e4SLinus Torvalds iosapic_startup_level_irq (unsigned int irq) 3781da177e4SLinus Torvalds { 3791da177e4SLinus Torvalds unmask_irq(irq); 3801da177e4SLinus Torvalds return 0; 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds static void 3841da177e4SLinus Torvalds iosapic_end_level_irq (unsigned int irq) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds ia64_vector vec = irq_to_vector(irq); 38724eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds move_irq(irq); 39024eeb568SKenji Kaneshige list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) 39124eeb568SKenji Kaneshige iosapic_eoi(rte->addr, vec); 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds #define iosapic_shutdown_level_irq mask_irq 3951da177e4SLinus Torvalds #define iosapic_enable_level_irq unmask_irq 3961da177e4SLinus Torvalds #define iosapic_disable_level_irq mask_irq 3971da177e4SLinus Torvalds #define iosapic_ack_level_irq nop 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds struct hw_interrupt_type irq_type_iosapic_level = { 4001da177e4SLinus Torvalds .typename = "IO-SAPIC-level", 4011da177e4SLinus Torvalds .startup = iosapic_startup_level_irq, 4021da177e4SLinus Torvalds .shutdown = iosapic_shutdown_level_irq, 4031da177e4SLinus Torvalds .enable = iosapic_enable_level_irq, 4041da177e4SLinus Torvalds .disable = iosapic_disable_level_irq, 4051da177e4SLinus Torvalds .ack = iosapic_ack_level_irq, 4061da177e4SLinus Torvalds .end = iosapic_end_level_irq, 4071da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4081da177e4SLinus Torvalds }; 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds /* 4111da177e4SLinus Torvalds * Handlers for edge-triggered interrupts. 4121da177e4SLinus Torvalds */ 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds static unsigned int 4151da177e4SLinus Torvalds iosapic_startup_edge_irq (unsigned int irq) 4161da177e4SLinus Torvalds { 4171da177e4SLinus Torvalds unmask_irq(irq); 4181da177e4SLinus Torvalds /* 4191da177e4SLinus Torvalds * IOSAPIC simply drops interrupts pended while the 4201da177e4SLinus Torvalds * corresponding pin was masked, so we can't know if an 4211da177e4SLinus Torvalds * interrupt is pending already. Let's hope not... 4221da177e4SLinus Torvalds */ 4231da177e4SLinus Torvalds return 0; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds static void 4271da177e4SLinus Torvalds iosapic_ack_edge_irq (unsigned int irq) 4281da177e4SLinus Torvalds { 4291da177e4SLinus Torvalds irq_desc_t *idesc = irq_descp(irq); 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds move_irq(irq); 4321da177e4SLinus Torvalds /* 4331da177e4SLinus Torvalds * Once we have recorded IRQ_PENDING already, we can mask the 4341da177e4SLinus Torvalds * interrupt for real. This prevents IRQ storms from unhandled 4351da177e4SLinus Torvalds * devices. 4361da177e4SLinus Torvalds */ 4371da177e4SLinus Torvalds if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED)) 4381da177e4SLinus Torvalds mask_irq(irq); 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds #define iosapic_enable_edge_irq unmask_irq 4421da177e4SLinus Torvalds #define iosapic_disable_edge_irq nop 4431da177e4SLinus Torvalds #define iosapic_end_edge_irq nop 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds struct hw_interrupt_type irq_type_iosapic_edge = { 4461da177e4SLinus Torvalds .typename = "IO-SAPIC-edge", 4471da177e4SLinus Torvalds .startup = iosapic_startup_edge_irq, 4481da177e4SLinus Torvalds .shutdown = iosapic_disable_edge_irq, 4491da177e4SLinus Torvalds .enable = iosapic_enable_edge_irq, 4501da177e4SLinus Torvalds .disable = iosapic_disable_edge_irq, 4511da177e4SLinus Torvalds .ack = iosapic_ack_edge_irq, 4521da177e4SLinus Torvalds .end = iosapic_end_edge_irq, 4531da177e4SLinus Torvalds .set_affinity = iosapic_set_affinity 4541da177e4SLinus Torvalds }; 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds unsigned int 4571da177e4SLinus Torvalds iosapic_version (char __iomem *addr) 4581da177e4SLinus Torvalds { 4591da177e4SLinus Torvalds /* 4601da177e4SLinus Torvalds * IOSAPIC Version Register return 32 bit structure like: 4611da177e4SLinus Torvalds * { 4621da177e4SLinus Torvalds * unsigned int version : 8; 4631da177e4SLinus Torvalds * unsigned int reserved1 : 8; 4641da177e4SLinus Torvalds * unsigned int max_redir : 8; 4651da177e4SLinus Torvalds * unsigned int reserved2 : 8; 4661da177e4SLinus Torvalds * } 4671da177e4SLinus Torvalds */ 4681da177e4SLinus Torvalds return iosapic_read(addr, IOSAPIC_VERSION); 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 47124eeb568SKenji Kaneshige static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol) 47224eeb568SKenji Kaneshige { 47324eeb568SKenji Kaneshige int i, vector = -1, min_count = -1; 47424eeb568SKenji Kaneshige struct iosapic_intr_info *info; 47524eeb568SKenji Kaneshige 47624eeb568SKenji Kaneshige /* 47724eeb568SKenji Kaneshige * shared vectors for edge-triggered interrupts are not 47824eeb568SKenji Kaneshige * supported yet 47924eeb568SKenji Kaneshige */ 48024eeb568SKenji Kaneshige if (trigger == IOSAPIC_EDGE) 48124eeb568SKenji Kaneshige return -1; 48224eeb568SKenji Kaneshige 48324eeb568SKenji Kaneshige for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) { 48424eeb568SKenji Kaneshige info = &iosapic_intr_info[i]; 48524eeb568SKenji Kaneshige if (info->trigger == trigger && info->polarity == pol && 48624eeb568SKenji Kaneshige (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) { 48724eeb568SKenji Kaneshige if (min_count == -1 || info->count < min_count) { 48824eeb568SKenji Kaneshige vector = i; 48924eeb568SKenji Kaneshige min_count = info->count; 49024eeb568SKenji Kaneshige } 49124eeb568SKenji Kaneshige } 49224eeb568SKenji Kaneshige } 49324eeb568SKenji Kaneshige if (vector < 0) 49424eeb568SKenji Kaneshige panic("%s: out of interrupt vectors!\n", __FUNCTION__); 49524eeb568SKenji Kaneshige 49624eeb568SKenji Kaneshige return vector; 49724eeb568SKenji Kaneshige } 49824eeb568SKenji Kaneshige 4991da177e4SLinus Torvalds /* 5001da177e4SLinus Torvalds * if the given vector is already owned by other, 5011da177e4SLinus Torvalds * assign a new vector for the other and make the vector available 5021da177e4SLinus Torvalds */ 5031da177e4SLinus Torvalds static void __init 5041da177e4SLinus Torvalds iosapic_reassign_vector (int vector) 5051da177e4SLinus Torvalds { 5061da177e4SLinus Torvalds int new_vector; 5071da177e4SLinus Torvalds 50824eeb568SKenji Kaneshige if (!list_empty(&iosapic_intr_info[vector].rtes)) { 5091da177e4SLinus Torvalds new_vector = assign_irq_vector(AUTO_ASSIGN); 5101da177e4SLinus Torvalds printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); 5111da177e4SLinus Torvalds memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], 5121da177e4SLinus Torvalds sizeof(struct iosapic_intr_info)); 51324eeb568SKenji Kaneshige INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes); 51424eeb568SKenji Kaneshige list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes); 5151da177e4SLinus Torvalds memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); 51624eeb568SKenji Kaneshige iosapic_intr_info[vector].low32 = IOSAPIC_MASK; 51724eeb568SKenji Kaneshige INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); 5181da177e4SLinus Torvalds } 5191da177e4SLinus Torvalds } 5201da177e4SLinus Torvalds 52124eeb568SKenji Kaneshige static struct iosapic_rte_info *iosapic_alloc_rte (void) 52224eeb568SKenji Kaneshige { 52324eeb568SKenji Kaneshige int i; 52424eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 52524eeb568SKenji Kaneshige int preallocated = 0; 52624eeb568SKenji Kaneshige 52724eeb568SKenji Kaneshige if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { 52824eeb568SKenji Kaneshige rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES); 52924eeb568SKenji Kaneshige if (!rte) 53024eeb568SKenji Kaneshige return NULL; 53124eeb568SKenji Kaneshige for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) 53224eeb568SKenji Kaneshige list_add(&rte->rte_list, &free_rte_list); 53324eeb568SKenji Kaneshige } 53424eeb568SKenji Kaneshige 53524eeb568SKenji Kaneshige if (!list_empty(&free_rte_list)) { 53624eeb568SKenji Kaneshige rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list); 53724eeb568SKenji Kaneshige list_del(&rte->rte_list); 53824eeb568SKenji Kaneshige preallocated++; 53924eeb568SKenji Kaneshige } else { 54024eeb568SKenji Kaneshige rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC); 54124eeb568SKenji Kaneshige if (!rte) 54224eeb568SKenji Kaneshige return NULL; 54324eeb568SKenji Kaneshige } 54424eeb568SKenji Kaneshige 54524eeb568SKenji Kaneshige memset(rte, 0, sizeof(struct iosapic_rte_info)); 54624eeb568SKenji Kaneshige if (preallocated) 54724eeb568SKenji Kaneshige rte->flags |= RTE_PREALLOCATED; 54824eeb568SKenji Kaneshige 54924eeb568SKenji Kaneshige return rte; 55024eeb568SKenji Kaneshige } 55124eeb568SKenji Kaneshige 55224eeb568SKenji Kaneshige static void iosapic_free_rte (struct iosapic_rte_info *rte) 55324eeb568SKenji Kaneshige { 55424eeb568SKenji Kaneshige if (rte->flags & RTE_PREALLOCATED) 55524eeb568SKenji Kaneshige list_add_tail(&rte->rte_list, &free_rte_list); 55624eeb568SKenji Kaneshige else 55724eeb568SKenji Kaneshige kfree(rte); 55824eeb568SKenji Kaneshige } 55924eeb568SKenji Kaneshige 56024eeb568SKenji Kaneshige static inline int vector_is_shared (int vector) 56124eeb568SKenji Kaneshige { 56224eeb568SKenji Kaneshige return (iosapic_intr_info[vector].count > 1); 56324eeb568SKenji Kaneshige } 56424eeb568SKenji Kaneshige 5651da177e4SLinus Torvalds static void 5661da177e4SLinus Torvalds register_intr (unsigned int gsi, int vector, unsigned char delivery, 5671da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 5681da177e4SLinus Torvalds { 5691da177e4SLinus Torvalds irq_desc_t *idesc; 5701da177e4SLinus Torvalds struct hw_interrupt_type *irq_type; 5711da177e4SLinus Torvalds int rte_index; 5721da177e4SLinus Torvalds int index; 5731da177e4SLinus Torvalds unsigned long gsi_base; 5741da177e4SLinus Torvalds void __iomem *iosapic_address; 57524eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds index = find_iosapic(gsi); 5781da177e4SLinus Torvalds if (index < 0) { 5791da177e4SLinus Torvalds printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi); 5801da177e4SLinus Torvalds return; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds iosapic_address = iosapic_lists[index].addr; 5841da177e4SLinus Torvalds gsi_base = iosapic_lists[index].gsi_base; 5851da177e4SLinus Torvalds 58624eeb568SKenji Kaneshige rte = gsi_vector_to_rte(gsi, vector); 58724eeb568SKenji Kaneshige if (!rte) { 58824eeb568SKenji Kaneshige rte = iosapic_alloc_rte(); 58924eeb568SKenji Kaneshige if (!rte) { 59024eeb568SKenji Kaneshige printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__); 59124eeb568SKenji Kaneshige return; 59224eeb568SKenji Kaneshige } 59324eeb568SKenji Kaneshige 5941da177e4SLinus Torvalds rte_index = gsi - gsi_base; 59524eeb568SKenji Kaneshige rte->rte_index = rte_index; 59624eeb568SKenji Kaneshige rte->addr = iosapic_address; 59724eeb568SKenji Kaneshige rte->gsi_base = gsi_base; 59824eeb568SKenji Kaneshige rte->refcnt++; 59924eeb568SKenji Kaneshige list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes); 60024eeb568SKenji Kaneshige iosapic_intr_info[vector].count++; 60124eeb568SKenji Kaneshige } 60224eeb568SKenji Kaneshige else if (vector_is_shared(vector)) { 60324eeb568SKenji Kaneshige struct iosapic_intr_info *info = &iosapic_intr_info[vector]; 60424eeb568SKenji Kaneshige if (info->trigger != trigger || info->polarity != polarity) { 60524eeb568SKenji Kaneshige printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__); 60624eeb568SKenji Kaneshige return; 60724eeb568SKenji Kaneshige } 60824eeb568SKenji Kaneshige } 60924eeb568SKenji Kaneshige 6101da177e4SLinus Torvalds iosapic_intr_info[vector].polarity = polarity; 6111da177e4SLinus Torvalds iosapic_intr_info[vector].dmode = delivery; 6121da177e4SLinus Torvalds iosapic_intr_info[vector].trigger = trigger; 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds if (trigger == IOSAPIC_EDGE) 6151da177e4SLinus Torvalds irq_type = &irq_type_iosapic_edge; 6161da177e4SLinus Torvalds else 6171da177e4SLinus Torvalds irq_type = &irq_type_iosapic_level; 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds idesc = irq_descp(vector); 6201da177e4SLinus Torvalds if (idesc->handler != irq_type) { 6211da177e4SLinus Torvalds if (idesc->handler != &no_irq_type) 6221da177e4SLinus Torvalds printk(KERN_WARNING "%s: changing vector %d from %s to %s\n", 6231da177e4SLinus Torvalds __FUNCTION__, vector, idesc->handler->typename, irq_type->typename); 6241da177e4SLinus Torvalds idesc->handler = irq_type; 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds static unsigned int 6291da177e4SLinus Torvalds get_target_cpu (unsigned int gsi, int vector) 6301da177e4SLinus Torvalds { 6311da177e4SLinus Torvalds #ifdef CONFIG_SMP 6321da177e4SLinus Torvalds static int cpu = -1; 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds /* 63524eeb568SKenji Kaneshige * In case of vector shared by multiple RTEs, all RTEs that 63624eeb568SKenji Kaneshige * share the vector need to use the same destination CPU. 63724eeb568SKenji Kaneshige */ 63824eeb568SKenji Kaneshige if (!list_empty(&iosapic_intr_info[vector].rtes)) 63924eeb568SKenji Kaneshige return iosapic_intr_info[vector].dest; 64024eeb568SKenji Kaneshige 64124eeb568SKenji Kaneshige /* 6421da177e4SLinus Torvalds * If the platform supports redirection via XTP, let it 6431da177e4SLinus Torvalds * distribute interrupts. 6441da177e4SLinus Torvalds */ 6451da177e4SLinus Torvalds if (smp_int_redirect & SMP_IRQ_REDIRECTION) 6461da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds /* 6491da177e4SLinus Torvalds * Some interrupts (ACPI SCI, for instance) are registered 6501da177e4SLinus Torvalds * before the BSP is marked as online. 6511da177e4SLinus Torvalds */ 6521da177e4SLinus Torvalds if (!cpu_online(smp_processor_id())) 6531da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds #ifdef CONFIG_NUMA 6561da177e4SLinus Torvalds { 6571da177e4SLinus Torvalds int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0; 6581da177e4SLinus Torvalds cpumask_t cpu_mask; 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds iosapic_index = find_iosapic(gsi); 6611da177e4SLinus Torvalds if (iosapic_index < 0 || 6621da177e4SLinus Torvalds iosapic_lists[iosapic_index].node == MAX_NUMNODES) 6631da177e4SLinus Torvalds goto skip_numa_setup; 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node); 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds for_each_cpu_mask(numa_cpu, cpu_mask) { 6681da177e4SLinus Torvalds if (!cpu_online(numa_cpu)) 6691da177e4SLinus Torvalds cpu_clear(numa_cpu, cpu_mask); 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds num_cpus = cpus_weight(cpu_mask); 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds if (!num_cpus) 6751da177e4SLinus Torvalds goto skip_numa_setup; 6761da177e4SLinus Torvalds 6771da177e4SLinus Torvalds /* Use vector assigment to distribute across cpus in node */ 6781da177e4SLinus Torvalds cpu_index = vector % num_cpus; 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++) 6811da177e4SLinus Torvalds numa_cpu = next_cpu(numa_cpu, cpu_mask); 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds if (numa_cpu != NR_CPUS) 6841da177e4SLinus Torvalds return cpu_physical_id(numa_cpu); 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds skip_numa_setup: 6871da177e4SLinus Torvalds #endif 6881da177e4SLinus Torvalds /* 6891da177e4SLinus Torvalds * Otherwise, round-robin interrupt vectors across all the 6901da177e4SLinus Torvalds * processors. (It'd be nice if we could be smarter in the 6911da177e4SLinus Torvalds * case of NUMA.) 6921da177e4SLinus Torvalds */ 6931da177e4SLinus Torvalds do { 6941da177e4SLinus Torvalds if (++cpu >= NR_CPUS) 6951da177e4SLinus Torvalds cpu = 0; 6961da177e4SLinus Torvalds } while (!cpu_online(cpu)); 6971da177e4SLinus Torvalds 6981da177e4SLinus Torvalds return cpu_physical_id(cpu); 6991da177e4SLinus Torvalds #else 7001da177e4SLinus Torvalds return cpu_physical_id(smp_processor_id()); 7011da177e4SLinus Torvalds #endif 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds /* 7051da177e4SLinus Torvalds * ACPI can describe IOSAPIC interrupts via static tables and namespace 7061da177e4SLinus Torvalds * methods. This provides an interface to register those interrupts and 7071da177e4SLinus Torvalds * program the IOSAPIC RTE. 7081da177e4SLinus Torvalds */ 7091da177e4SLinus Torvalds int 7101da177e4SLinus Torvalds iosapic_register_intr (unsigned int gsi, 7111da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 7121da177e4SLinus Torvalds { 71324eeb568SKenji Kaneshige int vector, mask = 1; 7141da177e4SLinus Torvalds unsigned int dest; 7151da177e4SLinus Torvalds unsigned long flags; 71624eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 71724eeb568SKenji Kaneshige u32 low32; 71824eeb568SKenji Kaneshige again: 7191da177e4SLinus Torvalds /* 7201da177e4SLinus Torvalds * If this GSI has already been registered (i.e., it's a 7211da177e4SLinus Torvalds * shared interrupt, or we lost a race to register it), 7221da177e4SLinus Torvalds * don't touch the RTE. 7231da177e4SLinus Torvalds */ 7241da177e4SLinus Torvalds spin_lock_irqsave(&iosapic_lock, flags); 7251da177e4SLinus Torvalds { 7261da177e4SLinus Torvalds vector = gsi_to_vector(gsi); 7271da177e4SLinus Torvalds if (vector > 0) { 72824eeb568SKenji Kaneshige rte = gsi_vector_to_rte(gsi, vector); 72924eeb568SKenji Kaneshige rte->refcnt++; 7301da177e4SLinus Torvalds spin_unlock_irqrestore(&iosapic_lock, flags); 7311da177e4SLinus Torvalds return vector; 7321da177e4SLinus Torvalds } 73324eeb568SKenji Kaneshige } 73424eeb568SKenji Kaneshige spin_unlock_irqrestore(&iosapic_lock, flags); 7351da177e4SLinus Torvalds 73624eeb568SKenji Kaneshige /* If vector is running out, we try to find a sharable vector */ 73724eeb568SKenji Kaneshige vector = assign_irq_vector_nopanic(AUTO_ASSIGN); 73824eeb568SKenji Kaneshige if (vector < 0) 73924eeb568SKenji Kaneshige vector = iosapic_find_sharable_vector(trigger, polarity); 74024eeb568SKenji Kaneshige 74124eeb568SKenji Kaneshige spin_lock_irqsave(&irq_descp(vector)->lock, flags); 74224eeb568SKenji Kaneshige spin_lock(&iosapic_lock); 74324eeb568SKenji Kaneshige { 74424eeb568SKenji Kaneshige if (gsi_to_vector(gsi) > 0) { 74524eeb568SKenji Kaneshige if (list_empty(&iosapic_intr_info[vector].rtes)) 74624eeb568SKenji Kaneshige free_irq_vector(vector); 74724eeb568SKenji Kaneshige spin_unlock(&iosapic_lock); 74824eeb568SKenji Kaneshige spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); 74924eeb568SKenji Kaneshige goto again; 75024eeb568SKenji Kaneshige } 75124eeb568SKenji Kaneshige 7521da177e4SLinus Torvalds dest = get_target_cpu(gsi, vector); 7531da177e4SLinus Torvalds register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, 7541da177e4SLinus Torvalds polarity, trigger); 7551da177e4SLinus Torvalds 75624eeb568SKenji Kaneshige /* 75724eeb568SKenji Kaneshige * If the vector is shared and already unmasked for 75824eeb568SKenji Kaneshige * other interrupt sources, don't mask it. 75924eeb568SKenji Kaneshige */ 76024eeb568SKenji Kaneshige low32 = iosapic_intr_info[vector].low32; 76124eeb568SKenji Kaneshige if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK)) 76224eeb568SKenji Kaneshige mask = 0; 76324eeb568SKenji Kaneshige set_rte(gsi, vector, dest, mask); 7641da177e4SLinus Torvalds } 765b9e41d7fSKenji Kaneshige spin_unlock(&iosapic_lock); 76624eeb568SKenji Kaneshige spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", 7691da177e4SLinus Torvalds gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 7701da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 7711da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 7721da177e4SLinus Torvalds 7731da177e4SLinus Torvalds return vector; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds #ifdef CONFIG_ACPI_DEALLOCATE_IRQ 7771da177e4SLinus Torvalds void 7781da177e4SLinus Torvalds iosapic_unregister_intr (unsigned int gsi) 7791da177e4SLinus Torvalds { 7801da177e4SLinus Torvalds unsigned long flags; 7811da177e4SLinus Torvalds int irq, vector; 7821da177e4SLinus Torvalds irq_desc_t *idesc; 78324eeb568SKenji Kaneshige u32 low32; 7841da177e4SLinus Torvalds unsigned long trigger, polarity; 78524eeb568SKenji Kaneshige unsigned int dest; 78624eeb568SKenji Kaneshige struct iosapic_rte_info *rte; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds /* 7891da177e4SLinus Torvalds * If the irq associated with the gsi is not found, 7901da177e4SLinus Torvalds * iosapic_unregister_intr() is unbalanced. We need to check 7911da177e4SLinus Torvalds * this again after getting locks. 7921da177e4SLinus Torvalds */ 7931da177e4SLinus Torvalds irq = gsi_to_irq(gsi); 7941da177e4SLinus Torvalds if (irq < 0) { 7951da177e4SLinus Torvalds printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); 7961da177e4SLinus Torvalds WARN_ON(1); 7971da177e4SLinus Torvalds return; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds vector = irq_to_vector(irq); 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds idesc = irq_descp(irq); 8021da177e4SLinus Torvalds spin_lock_irqsave(&idesc->lock, flags); 8031da177e4SLinus Torvalds spin_lock(&iosapic_lock); 8041da177e4SLinus Torvalds { 80524eeb568SKenji Kaneshige if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) { 8061da177e4SLinus Torvalds printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); 8071da177e4SLinus Torvalds WARN_ON(1); 80824eeb568SKenji Kaneshige goto out; 8091da177e4SLinus Torvalds } 8101da177e4SLinus Torvalds 81124eeb568SKenji Kaneshige if (--rte->refcnt > 0) 81224eeb568SKenji Kaneshige goto out; 8131da177e4SLinus Torvalds 81424eeb568SKenji Kaneshige /* Mask the interrupt */ 81524eeb568SKenji Kaneshige low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK; 81624eeb568SKenji Kaneshige iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32); 8171da177e4SLinus Torvalds 81824eeb568SKenji Kaneshige /* Remove the rte entry from the list */ 81924eeb568SKenji Kaneshige list_del(&rte->rte_list); 82024eeb568SKenji Kaneshige iosapic_intr_info[vector].count--; 82124eeb568SKenji Kaneshige iosapic_free_rte(rte); 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds trigger = iosapic_intr_info[vector].trigger; 8241da177e4SLinus Torvalds polarity = iosapic_intr_info[vector].polarity; 82524eeb568SKenji Kaneshige dest = iosapic_intr_info[vector].dest; 82624eeb568SKenji Kaneshige printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n", 82724eeb568SKenji Kaneshige gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 82824eeb568SKenji Kaneshige (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 82924eeb568SKenji Kaneshige cpu_logical_id(dest), dest, vector); 8301da177e4SLinus Torvalds 83124eeb568SKenji Kaneshige if (list_empty(&iosapic_intr_info[vector].rtes)) { 83224eeb568SKenji Kaneshige /* Sanity check */ 83324eeb568SKenji Kaneshige BUG_ON(iosapic_intr_info[vector].count); 83424eeb568SKenji Kaneshige 83524eeb568SKenji Kaneshige /* Clear the interrupt controller descriptor */ 83624eeb568SKenji Kaneshige idesc->handler = &no_irq_type; 83724eeb568SKenji Kaneshige 83824eeb568SKenji Kaneshige /* Clear the interrupt information */ 8391da177e4SLinus Torvalds memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); 84024eeb568SKenji Kaneshige iosapic_intr_info[vector].low32 |= IOSAPIC_MASK; 84124eeb568SKenji Kaneshige INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); 84224eeb568SKenji Kaneshige 84324eeb568SKenji Kaneshige if (idesc->action) { 84424eeb568SKenji Kaneshige printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq); 84524eeb568SKenji Kaneshige WARN_ON(1); 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds /* Free the interrupt vector */ 8491da177e4SLinus Torvalds free_irq_vector(vector); 85024eeb568SKenji Kaneshige } 85124eeb568SKenji Kaneshige } 85224eeb568SKenji Kaneshige out: 85324eeb568SKenji Kaneshige spin_unlock(&iosapic_lock); 85424eeb568SKenji Kaneshige spin_unlock_irqrestore(&idesc->lock, flags); 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds /* 8591da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a platform interrupt. 8601da177e4SLinus Torvalds * Note that the irq_base and IOSAPIC address must be set in iosapic_init(). 8611da177e4SLinus Torvalds */ 8621da177e4SLinus Torvalds int __init 8631da177e4SLinus Torvalds iosapic_register_platform_intr (u32 int_type, unsigned int gsi, 8641da177e4SLinus Torvalds int iosapic_vector, u16 eid, u16 id, 8651da177e4SLinus Torvalds unsigned long polarity, unsigned long trigger) 8661da177e4SLinus Torvalds { 8671da177e4SLinus Torvalds static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"}; 8681da177e4SLinus Torvalds unsigned char delivery; 8691da177e4SLinus Torvalds int vector, mask = 0; 8701da177e4SLinus Torvalds unsigned int dest = ((id << 8) | eid) & 0xffff; 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds switch (int_type) { 8731da177e4SLinus Torvalds case ACPI_INTERRUPT_PMI: 8741da177e4SLinus Torvalds vector = iosapic_vector; 8751da177e4SLinus Torvalds /* 8761da177e4SLinus Torvalds * since PMI vector is alloc'd by FW(ACPI) not by kernel, 8771da177e4SLinus Torvalds * we need to make sure the vector is available 8781da177e4SLinus Torvalds */ 8791da177e4SLinus Torvalds iosapic_reassign_vector(vector); 8801da177e4SLinus Torvalds delivery = IOSAPIC_PMI; 8811da177e4SLinus Torvalds break; 8821da177e4SLinus Torvalds case ACPI_INTERRUPT_INIT: 8831da177e4SLinus Torvalds vector = assign_irq_vector(AUTO_ASSIGN); 8841da177e4SLinus Torvalds delivery = IOSAPIC_INIT; 8851da177e4SLinus Torvalds break; 8861da177e4SLinus Torvalds case ACPI_INTERRUPT_CPEI: 8871da177e4SLinus Torvalds vector = IA64_CPE_VECTOR; 8881da177e4SLinus Torvalds delivery = IOSAPIC_LOWEST_PRIORITY; 8891da177e4SLinus Torvalds mask = 1; 8901da177e4SLinus Torvalds break; 8911da177e4SLinus Torvalds default: 8921da177e4SLinus Torvalds printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type); 8931da177e4SLinus Torvalds return -1; 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds register_intr(gsi, vector, delivery, polarity, trigger); 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", 8991da177e4SLinus Torvalds int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown", 9001da177e4SLinus Torvalds int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), 9011da177e4SLinus Torvalds (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), 9021da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9031da177e4SLinus Torvalds 90424eeb568SKenji Kaneshige set_rte(gsi, vector, dest, mask); 9051da177e4SLinus Torvalds return vector; 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds /* 9101da177e4SLinus Torvalds * ACPI calls this when it finds an entry for a legacy ISA IRQ override. 9111da177e4SLinus Torvalds * Note that the gsi_base and IOSAPIC address must be set in iosapic_init(). 9121da177e4SLinus Torvalds */ 9131da177e4SLinus Torvalds void __init 9141da177e4SLinus Torvalds iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, 9151da177e4SLinus Torvalds unsigned long polarity, 9161da177e4SLinus Torvalds unsigned long trigger) 9171da177e4SLinus Torvalds { 9181da177e4SLinus Torvalds int vector; 9191da177e4SLinus Torvalds unsigned int dest = cpu_physical_id(smp_processor_id()); 9201da177e4SLinus Torvalds 9211da177e4SLinus Torvalds vector = isa_irq_to_vector(isa_irq); 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger); 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n", 9261da177e4SLinus Torvalds isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level", 9271da177e4SLinus Torvalds polarity == IOSAPIC_POL_HIGH ? "high" : "low", 9281da177e4SLinus Torvalds cpu_logical_id(dest), dest, vector); 9291da177e4SLinus Torvalds 93024eeb568SKenji Kaneshige set_rte(gsi, vector, dest, 1); 9311da177e4SLinus Torvalds } 9321da177e4SLinus Torvalds 9331da177e4SLinus Torvalds void __init 9341da177e4SLinus Torvalds iosapic_system_init (int system_pcat_compat) 9351da177e4SLinus Torvalds { 9361da177e4SLinus Torvalds int vector; 9371da177e4SLinus Torvalds 93824eeb568SKenji Kaneshige for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) { 93924eeb568SKenji Kaneshige iosapic_intr_info[vector].low32 = IOSAPIC_MASK; 94024eeb568SKenji Kaneshige INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); /* mark as unused */ 94124eeb568SKenji Kaneshige } 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds pcat_compat = system_pcat_compat; 9441da177e4SLinus Torvalds if (pcat_compat) { 9451da177e4SLinus Torvalds /* 9461da177e4SLinus Torvalds * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support 9471da177e4SLinus Torvalds * enabled. 9481da177e4SLinus Torvalds */ 9491da177e4SLinus Torvalds printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__); 9501da177e4SLinus Torvalds outb(0xff, 0xA1); 9511da177e4SLinus Torvalds outb(0xff, 0x21); 9521da177e4SLinus Torvalds } 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds void __init 9561da177e4SLinus Torvalds iosapic_init (unsigned long phys_addr, unsigned int gsi_base) 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds int num_rte; 9591da177e4SLinus Torvalds unsigned int isa_irq, ver; 9601da177e4SLinus Torvalds char __iomem *addr; 9611da177e4SLinus Torvalds 9621da177e4SLinus Torvalds addr = ioremap(phys_addr, 0); 9631da177e4SLinus Torvalds ver = iosapic_version(addr); 9641da177e4SLinus Torvalds 9651da177e4SLinus Torvalds /* 9661da177e4SLinus Torvalds * The MAX_REDIR register holds the highest input pin 9671da177e4SLinus Torvalds * number (starting from 0). 9681da177e4SLinus Torvalds * We add 1 so that we can use it for number of pins (= RTEs) 9691da177e4SLinus Torvalds */ 9701da177e4SLinus Torvalds num_rte = ((ver >> 16) & 0xff) + 1; 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds iosapic_lists[num_iosapic].addr = addr; 9731da177e4SLinus Torvalds iosapic_lists[num_iosapic].gsi_base = gsi_base; 9741da177e4SLinus Torvalds iosapic_lists[num_iosapic].num_rte = num_rte; 9751da177e4SLinus Torvalds #ifdef CONFIG_NUMA 9761da177e4SLinus Torvalds iosapic_lists[num_iosapic].node = MAX_NUMNODES; 9771da177e4SLinus Torvalds #endif 9781da177e4SLinus Torvalds num_iosapic++; 9791da177e4SLinus Torvalds 9801da177e4SLinus Torvalds if ((gsi_base == 0) && pcat_compat) { 9811da177e4SLinus Torvalds /* 9821da177e4SLinus Torvalds * Map the legacy ISA devices into the IOSAPIC data. Some of these may 9831da177e4SLinus Torvalds * get reprogrammed later on with data from the ACPI Interrupt Source 9841da177e4SLinus Torvalds * Override table. 9851da177e4SLinus Torvalds */ 9861da177e4SLinus Torvalds for (isa_irq = 0; isa_irq < 16; ++isa_irq) 9871da177e4SLinus Torvalds iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE); 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds } 9901da177e4SLinus Torvalds 9911da177e4SLinus Torvalds #ifdef CONFIG_NUMA 9921da177e4SLinus Torvalds void __init 9931da177e4SLinus Torvalds map_iosapic_to_node(unsigned int gsi_base, int node) 9941da177e4SLinus Torvalds { 9951da177e4SLinus Torvalds int index; 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds index = find_iosapic(gsi_base); 9981da177e4SLinus Torvalds if (index < 0) { 9991da177e4SLinus Torvalds printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", 10001da177e4SLinus Torvalds __FUNCTION__, gsi_base); 10011da177e4SLinus Torvalds return; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds iosapic_lists[index].node = node; 10041da177e4SLinus Torvalds return; 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds #endif 100724eeb568SKenji Kaneshige 100824eeb568SKenji Kaneshige static int __init iosapic_enable_kmalloc (void) 100924eeb568SKenji Kaneshige { 101024eeb568SKenji Kaneshige iosapic_kmalloc_ok = 1; 101124eeb568SKenji Kaneshige return 0; 101224eeb568SKenji Kaneshige } 101324eeb568SKenji Kaneshige core_initcall (iosapic_enable_kmalloc); 1014