ia64/xen-unstable

annotate linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c @ 9747:de2dc4e7966a

[IA64] Add support to physdev_ops

Add support to physdev ops, and thus give IOSAPIC RTEs
managed by Xen now. Dom0 now issues hypercall to r/w
RTE entry. Another change is the irq vector allocation
which is also owned by xen now.

After this change, the IOSAPIC is almost owned by xen
with only exception as IOSAPIC EOI which is still issued
by dom0 directly. But that's OK since currently dom0
owns all external physical devices. Later full event
channel mechanism will provide necessary support for
driver domain, and at that time, dom0 instead issues
physdev_op (PHYSDEVOP_IRQ_UNMASK_NOTIFY) naturally as
replace of IOSAPIC EOI.

Signed-off-by Kevin Tian <kevin.tian@intel.com>
author awilliam@xenbuild.aw
date Fri Apr 21 09:03:19 2006 -0600 (2006-04-21)
parents 19148831ab05
children 42a8e3101c6c
rev   line source
awilliam@9745 1 /*
awilliam@9745 2 * I/O SAPIC support.
awilliam@9745 3 *
awilliam@9745 4 * Copyright (C) 1999 Intel Corp.
awilliam@9745 5 * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com>
awilliam@9745 6 * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com>
awilliam@9745 7 * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co.
awilliam@9745 8 * David Mosberger-Tang <davidm@hpl.hp.com>
awilliam@9745 9 * Copyright (C) 1999 VA Linux Systems
awilliam@9745 10 * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
awilliam@9745 11 *
awilliam@9745 12 * 00/04/19 D. Mosberger Rewritten to mirror more closely the x86 I/O APIC code.
awilliam@9745 13 * In particular, we now have separate handlers for edge
awilliam@9745 14 * and level triggered interrupts.
awilliam@9745 15 * 00/10/27 Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation
awilliam@9745 16 * PCI to vector mapping, shared PCI interrupts.
awilliam@9745 17 * 00/10/27 D. Mosberger Document things a bit more to make them more understandable.
awilliam@9745 18 * Clean up much of the old IOSAPIC cruft.
awilliam@9745 19 * 01/07/27 J.I. Lee PCI irq routing, Platform/Legacy interrupts and fixes for
awilliam@9745 20 * ACPI S5(SoftOff) support.
awilliam@9745 21 * 02/01/23 J.I. Lee iosapic pgm fixes for PCI irq routing from _PRT
awilliam@9745 22 * 02/01/07 E. Focht <efocht@ess.nec.de> Redirectable interrupt vectors in
awilliam@9745 23 * iosapic_set_affinity(), initializations for
awilliam@9745 24 * /proc/irq/#/smp_affinity
awilliam@9745 25 * 02/04/02 P. Diefenbaugh Cleaned up ACPI PCI IRQ routing.
awilliam@9745 26 * 02/04/18 J.I. Lee bug fix in iosapic_init_pci_irq
awilliam@9745 27 * 02/04/30 J.I. Lee bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping
awilliam@9745 28 * error
awilliam@9745 29 * 02/07/29 T. Kochi Allocate interrupt vectors dynamically
awilliam@9745 30 * 02/08/04 T. Kochi Cleaned up terminology (irq, global system interrupt, vector, etc.)
awilliam@9745 31 * 02/09/20 D. Mosberger Simplified by taking advantage of ACPI's pci_irq code.
awilliam@9745 32 * 03/02/19 B. Helgaas Make pcat_compat system-wide, not per-IOSAPIC.
awilliam@9745 33 * Remove iosapic_address & gsi_base from external interfaces.
awilliam@9745 34 * Rationalize __init/__devinit attributes.
awilliam@9745 35 * 04/12/04 Ashok Raj <ashok.raj@intel.com> Intel Corporation 2004
awilliam@9745 36 * Updated to work with irq migration necessary for CPU Hotplug
awilliam@9745 37 */
awilliam@9745 38 /*
awilliam@9745 39 * Here is what the interrupt logic between a PCI device and the kernel looks like:
awilliam@9745 40 *
awilliam@9745 41 * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD). The
awilliam@9745 42 * device is uniquely identified by its bus--, and slot-number (the function
awilliam@9745 43 * number does not matter here because all functions share the same interrupt
awilliam@9745 44 * lines).
awilliam@9745 45 *
awilliam@9745 46 * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller.
awilliam@9745 47 * Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level
awilliam@9745 48 * triggered and use the same polarity). Each interrupt line has a unique Global
awilliam@9745 49 * System Interrupt (GSI) number which can be calculated as the sum of the controller's
awilliam@9745 50 * base GSI number and the IOSAPIC pin number to which the line connects.
awilliam@9745 51 *
awilliam@9745 52 * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin
awilliam@9745 53 * into the IA-64 interrupt vector. This interrupt vector is then sent to the CPU.
awilliam@9745 54 *
awilliam@9745 55 * (4) The kernel recognizes an interrupt as an IRQ. The IRQ interface is used as
awilliam@9745 56 * architecture-independent interrupt handling mechanism in Linux. As an
awilliam@9745 57 * IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number
awilliam@9745 58 * mapping. On smaller systems, we use one-to-one mapping between IA-64 vector and
awilliam@9745 59 * IRQ. A platform can implement platform_irq_to_vector(irq) and
awilliam@9745 60 * platform_local_vector_to_irq(vector) APIs to differentiate the mapping.
awilliam@9745 61 * Please see also include/asm-ia64/hw_irq.h for those APIs.
awilliam@9745 62 *
awilliam@9745 63 * To sum up, there are three levels of mappings involved:
awilliam@9745 64 *
awilliam@9745 65 * PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ
awilliam@9745 66 *
awilliam@9745 67 * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts.
awilliam@9745 68 * Now we use "IRQ" only for Linux IRQ's. ISA IRQ (isa_irq) is the only exception in this
awilliam@9745 69 * source code.
awilliam@9745 70 */
awilliam@9745 71 #include <linux/config.h>
awilliam@9745 72
awilliam@9745 73 #include <linux/acpi.h>
awilliam@9745 74 #include <linux/init.h>
awilliam@9745 75 #include <linux/irq.h>
awilliam@9745 76 #include <linux/kernel.h>
awilliam@9745 77 #include <linux/list.h>
awilliam@9745 78 #include <linux/pci.h>
awilliam@9745 79 #include <linux/smp.h>
awilliam@9745 80 #include <linux/smp_lock.h>
awilliam@9745 81 #include <linux/string.h>
awilliam@9745 82 #include <linux/bootmem.h>
awilliam@9745 83
awilliam@9745 84 #include <asm/delay.h>
awilliam@9745 85 #include <asm/hw_irq.h>
awilliam@9745 86 #include <asm/io.h>
awilliam@9745 87 #include <asm/iosapic.h>
awilliam@9745 88 #include <asm/machvec.h>
awilliam@9745 89 #include <asm/processor.h>
awilliam@9745 90 #include <asm/ptrace.h>
awilliam@9745 91 #include <asm/system.h>
awilliam@9745 92
awilliam@9745 93
awilliam@9745 94 #undef DEBUG_INTERRUPT_ROUTING
awilliam@9745 95
awilliam@9745 96 #ifdef DEBUG_INTERRUPT_ROUTING
awilliam@9745 97 #define DBG(fmt...) printk(fmt)
awilliam@9745 98 #else
awilliam@9745 99 #define DBG(fmt...)
awilliam@9745 100 #endif
awilliam@9745 101
awilliam@9745 102 #define NR_PREALLOCATE_RTE_ENTRIES (PAGE_SIZE / sizeof(struct iosapic_rte_info))
awilliam@9745 103 #define RTE_PREALLOCATED (1)
awilliam@9745 104
awilliam@9745 105 static DEFINE_SPINLOCK(iosapic_lock);
awilliam@9745 106
awilliam@9745 107 /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */
awilliam@9745 108
awilliam@9745 109 struct iosapic_rte_info {
awilliam@9745 110 struct list_head rte_list; /* node in list of RTEs sharing the same vector */
awilliam@9745 111 char __iomem *addr; /* base address of IOSAPIC */
awilliam@9745 112 unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */
awilliam@9745 113 char rte_index; /* IOSAPIC RTE index */
awilliam@9745 114 int refcnt; /* reference counter */
awilliam@9745 115 unsigned int flags; /* flags */
awilliam@9745 116 } ____cacheline_aligned;
awilliam@9745 117
awilliam@9745 118 static struct iosapic_intr_info {
awilliam@9745 119 struct list_head rtes; /* RTEs using this vector (empty => not an IOSAPIC interrupt) */
awilliam@9745 120 int count; /* # of RTEs that shares this vector */
awilliam@9745 121 u32 low32; /* current value of low word of Redirection table entry */
awilliam@9745 122 unsigned int dest; /* destination CPU physical ID */
awilliam@9745 123 unsigned char dmode : 3; /* delivery mode (see iosapic.h) */
awilliam@9745 124 unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */
awilliam@9745 125 unsigned char trigger : 1; /* trigger mode (see iosapic.h) */
awilliam@9745 126 } iosapic_intr_info[IA64_NUM_VECTORS];
awilliam@9745 127
awilliam@9745 128 static struct iosapic {
awilliam@9745 129 char __iomem *addr; /* base address of IOSAPIC */
awilliam@9745 130 unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */
awilliam@9745 131 unsigned short num_rte; /* number of RTE in this IOSAPIC */
awilliam@9745 132 int rtes_inuse; /* # of RTEs in use on this IOSAPIC */
awilliam@9745 133 #ifdef CONFIG_NUMA
awilliam@9745 134 unsigned short node; /* numa node association via pxm */
awilliam@9745 135 #endif
awilliam@9745 136 } iosapic_lists[NR_IOSAPICS];
awilliam@9745 137
awilliam@9745 138 static unsigned char pcat_compat __devinitdata; /* 8259 compatibility flag */
awilliam@9745 139
awilliam@9745 140 static int iosapic_kmalloc_ok;
awilliam@9745 141 static LIST_HEAD(free_rte_list);
awilliam@9745 142
awilliam@9747 143 #ifdef CONFIG_XEN
awilliam@9747 144 #include <xen/interface/xen.h>
awilliam@9747 145 #include <xen/interface/physdev.h>
awilliam@9747 146 #include <asm/hypervisor.h>
awilliam@9747 147 static inline unsigned int xen_iosapic_read(char __iomem *iosapic, unsigned int reg)
awilliam@9747 148 {
awilliam@9747 149 physdev_op_t op;
awilliam@9747 150 int ret;
awilliam@9747 151
awilliam@9747 152 op.cmd = PHYSDEVOP_APIC_READ;
awilliam@9747 153 op.u.apic_op.apic_physbase = (unsigned long)iosapic -
awilliam@9747 154 __IA64_UNCACHED_OFFSET;
awilliam@9747 155 op.u.apic_op.reg = reg;
awilliam@9747 156 ret = HYPERVISOR_physdev_op(&op);
awilliam@9747 157 if (ret)
awilliam@9747 158 return ret;
awilliam@9747 159 return op.u.apic_op.value;
awilliam@9747 160 }
awilliam@9747 161
awilliam@9747 162 static inline void xen_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
awilliam@9747 163 {
awilliam@9747 164 physdev_op_t op;
awilliam@9747 165
awilliam@9747 166 op.cmd = PHYSDEVOP_APIC_WRITE;
awilliam@9747 167 op.u.apic_op.apic_physbase = (unsigned long)iosapic -
awilliam@9747 168 __IA64_UNCACHED_OFFSET;
awilliam@9747 169 op.u.apic_op.reg = reg;
awilliam@9747 170 op.u.apic_op.value = val;
awilliam@9747 171 HYPERVISOR_physdev_op(&op);
awilliam@9747 172 }
awilliam@9747 173
awilliam@9747 174 static inline unsigned int iosapic_read(char __iomem *iosapic, unsigned int reg)
awilliam@9747 175 {
awilliam@9747 176 if (!running_on_xen) {
awilliam@9747 177 writel(reg, iosapic + IOSAPIC_REG_SELECT);
awilliam@9747 178 return readl(iosapic + IOSAPIC_WINDOW);
awilliam@9747 179 } else
awilliam@9747 180 return xen_iosapic_read(iosapic, reg);
awilliam@9747 181 }
awilliam@9747 182
awilliam@9747 183 static inline void iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
awilliam@9747 184 {
awilliam@9747 185 if (!running_on_xen) {
awilliam@9747 186 writel(reg, iosapic + IOSAPIC_REG_SELECT);
awilliam@9747 187 writel(val, iosapic + IOSAPIC_WINDOW);
awilliam@9747 188 } else
awilliam@9747 189 xen_iosapic_write(iosapic, reg, val);
awilliam@9747 190 }
awilliam@9747 191
awilliam@9747 192 int xen_assign_irq_vector(int irq)
awilliam@9747 193 {
awilliam@9747 194 physdev_op_t op;
awilliam@9747 195
awilliam@9747 196 op.cmd = PHYSDEVOP_ASSIGN_VECTOR;
awilliam@9747 197 op.u.irq_op.irq = irq;
awilliam@9747 198 if (HYPERVISOR_physdev_op(&op))
awilliam@9747 199 return -ENOSPC;
awilliam@9747 200
awilliam@9747 201 return op.u.irq_op.vector;
awilliam@9747 202 }
awilliam@9747 203 #endif /* XEN */
awilliam@9747 204
awilliam@9745 205 /*
awilliam@9745 206 * Find an IOSAPIC associated with a GSI
awilliam@9745 207 */
awilliam@9745 208 static inline int
awilliam@9745 209 find_iosapic (unsigned int gsi)
awilliam@9745 210 {
awilliam@9745 211 int i;
awilliam@9745 212
awilliam@9745 213 for (i = 0; i < NR_IOSAPICS; i++) {
awilliam@9745 214 if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte)
awilliam@9745 215 return i;
awilliam@9745 216 }
awilliam@9745 217
awilliam@9745 218 return -1;
awilliam@9745 219 }
awilliam@9745 220
awilliam@9745 221 static inline int
awilliam@9745 222 _gsi_to_vector (unsigned int gsi)
awilliam@9745 223 {
awilliam@9745 224 struct iosapic_intr_info *info;
awilliam@9745 225 struct iosapic_rte_info *rte;
awilliam@9745 226
awilliam@9745 227 for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)
awilliam@9745 228 list_for_each_entry(rte, &info->rtes, rte_list)
awilliam@9745 229 if (rte->gsi_base + rte->rte_index == gsi)
awilliam@9745 230 return info - iosapic_intr_info;
awilliam@9745 231 return -1;
awilliam@9745 232 }
awilliam@9745 233
awilliam@9745 234 /*
awilliam@9745 235 * Translate GSI number to the corresponding IA-64 interrupt vector. If no
awilliam@9745 236 * entry exists, return -1.
awilliam@9745 237 */
awilliam@9745 238 inline int
awilliam@9745 239 gsi_to_vector (unsigned int gsi)
awilliam@9745 240 {
awilliam@9745 241 return _gsi_to_vector(gsi);
awilliam@9745 242 }
awilliam@9745 243
awilliam@9745 244 int
awilliam@9745 245 gsi_to_irq (unsigned int gsi)
awilliam@9745 246 {
awilliam@9745 247 unsigned long flags;
awilliam@9745 248 int irq;
awilliam@9745 249 /*
awilliam@9745 250 * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq
awilliam@9745 251 * numbers...
awilliam@9745 252 */
awilliam@9745 253 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 254 {
awilliam@9745 255 irq = _gsi_to_vector(gsi);
awilliam@9745 256 }
awilliam@9745 257 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 258
awilliam@9745 259 return irq;
awilliam@9745 260 }
awilliam@9745 261
awilliam@9745 262 static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec)
awilliam@9745 263 {
awilliam@9745 264 struct iosapic_rte_info *rte;
awilliam@9745 265
awilliam@9745 266 list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
awilliam@9745 267 if (rte->gsi_base + rte->rte_index == gsi)
awilliam@9745 268 return rte;
awilliam@9745 269 return NULL;
awilliam@9745 270 }
awilliam@9745 271
awilliam@9745 272 static void
awilliam@9745 273 set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask)
awilliam@9745 274 {
awilliam@9745 275 unsigned long pol, trigger, dmode;
awilliam@9745 276 u32 low32, high32;
awilliam@9745 277 char __iomem *addr;
awilliam@9745 278 int rte_index;
awilliam@9745 279 char redir;
awilliam@9745 280 struct iosapic_rte_info *rte;
awilliam@9745 281
awilliam@9745 282 DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);
awilliam@9745 283
awilliam@9745 284 rte = gsi_vector_to_rte(gsi, vector);
awilliam@9745 285 if (!rte)
awilliam@9745 286 return; /* not an IOSAPIC interrupt */
awilliam@9745 287
awilliam@9745 288 rte_index = rte->rte_index;
awilliam@9745 289 addr = rte->addr;
awilliam@9745 290 pol = iosapic_intr_info[vector].polarity;
awilliam@9745 291 trigger = iosapic_intr_info[vector].trigger;
awilliam@9745 292 dmode = iosapic_intr_info[vector].dmode;
awilliam@9745 293
awilliam@9745 294 redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;
awilliam@9745 295
awilliam@9745 296 #ifdef CONFIG_SMP
awilliam@9745 297 {
awilliam@9745 298 unsigned int irq;
awilliam@9745 299
awilliam@9745 300 for (irq = 0; irq < NR_IRQS; ++irq)
awilliam@9745 301 if (irq_to_vector(irq) == vector) {
awilliam@9745 302 set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);
awilliam@9745 303 break;
awilliam@9745 304 }
awilliam@9745 305 }
awilliam@9745 306 #endif
awilliam@9745 307
awilliam@9745 308 low32 = ((pol << IOSAPIC_POLARITY_SHIFT) |
awilliam@9745 309 (trigger << IOSAPIC_TRIGGER_SHIFT) |
awilliam@9745 310 (dmode << IOSAPIC_DELIVERY_SHIFT) |
awilliam@9745 311 ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) |
awilliam@9745 312 vector);
awilliam@9745 313
awilliam@9745 314 /* dest contains both id and eid */
awilliam@9745 315 high32 = (dest << IOSAPIC_DEST_SHIFT);
awilliam@9745 316
awilliam@9745 317 iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
awilliam@9745 318 iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
awilliam@9745 319 iosapic_intr_info[vector].low32 = low32;
awilliam@9745 320 iosapic_intr_info[vector].dest = dest;
awilliam@9745 321 }
awilliam@9745 322
awilliam@9745 323 static void
awilliam@9745 324 nop (unsigned int vector)
awilliam@9745 325 {
awilliam@9745 326 /* do nothing... */
awilliam@9745 327 }
awilliam@9745 328
awilliam@9745 329 static void
awilliam@9745 330 mask_irq (unsigned int irq)
awilliam@9745 331 {
awilliam@9745 332 unsigned long flags;
awilliam@9745 333 char __iomem *addr;
awilliam@9745 334 u32 low32;
awilliam@9745 335 int rte_index;
awilliam@9745 336 ia64_vector vec = irq_to_vector(irq);
awilliam@9745 337 struct iosapic_rte_info *rte;
awilliam@9745 338
awilliam@9745 339 if (list_empty(&iosapic_intr_info[vec].rtes))
awilliam@9745 340 return; /* not an IOSAPIC interrupt! */
awilliam@9745 341
awilliam@9745 342 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 343 {
awilliam@9745 344 /* set only the mask bit */
awilliam@9745 345 low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
awilliam@9745 346 list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
awilliam@9745 347 addr = rte->addr;
awilliam@9745 348 rte_index = rte->rte_index;
awilliam@9745 349 iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
awilliam@9745 350 }
awilliam@9745 351 }
awilliam@9745 352 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 353 }
awilliam@9745 354
awilliam@9745 355 static void
awilliam@9745 356 unmask_irq (unsigned int irq)
awilliam@9745 357 {
awilliam@9745 358 unsigned long flags;
awilliam@9745 359 char __iomem *addr;
awilliam@9745 360 u32 low32;
awilliam@9745 361 int rte_index;
awilliam@9745 362 ia64_vector vec = irq_to_vector(irq);
awilliam@9745 363 struct iosapic_rte_info *rte;
awilliam@9745 364
awilliam@9745 365 if (list_empty(&iosapic_intr_info[vec].rtes))
awilliam@9745 366 return; /* not an IOSAPIC interrupt! */
awilliam@9745 367
awilliam@9745 368 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 369 {
awilliam@9745 370 low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
awilliam@9745 371 list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
awilliam@9745 372 addr = rte->addr;
awilliam@9745 373 rte_index = rte->rte_index;
awilliam@9745 374 iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
awilliam@9745 375 }
awilliam@9745 376 }
awilliam@9745 377 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 378 }
awilliam@9745 379
awilliam@9745 380
awilliam@9745 381 static void
awilliam@9745 382 iosapic_set_affinity (unsigned int irq, cpumask_t mask)
awilliam@9745 383 {
awilliam@9745 384 #ifdef CONFIG_SMP
awilliam@9745 385 unsigned long flags;
awilliam@9745 386 u32 high32, low32;
awilliam@9745 387 int dest, rte_index;
awilliam@9745 388 char __iomem *addr;
awilliam@9745 389 int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
awilliam@9745 390 ia64_vector vec;
awilliam@9745 391 struct iosapic_rte_info *rte;
awilliam@9745 392
awilliam@9745 393 irq &= (~IA64_IRQ_REDIRECTED);
awilliam@9745 394 vec = irq_to_vector(irq);
awilliam@9745 395
awilliam@9745 396 if (cpus_empty(mask))
awilliam@9745 397 return;
awilliam@9745 398
awilliam@9745 399 dest = cpu_physical_id(first_cpu(mask));
awilliam@9745 400
awilliam@9745 401 if (list_empty(&iosapic_intr_info[vec].rtes))
awilliam@9745 402 return; /* not an IOSAPIC interrupt */
awilliam@9745 403
awilliam@9745 404 set_irq_affinity_info(irq, dest, redir);
awilliam@9745 405
awilliam@9745 406 /* dest contains both id and eid */
awilliam@9745 407 high32 = dest << IOSAPIC_DEST_SHIFT;
awilliam@9745 408
awilliam@9745 409 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 410 {
awilliam@9745 411 low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);
awilliam@9745 412
awilliam@9745 413 if (redir)
awilliam@9745 414 /* change delivery mode to lowest priority */
awilliam@9745 415 low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
awilliam@9745 416 else
awilliam@9745 417 /* change delivery mode to fixed */
awilliam@9745 418 low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);
awilliam@9745 419
awilliam@9745 420 iosapic_intr_info[vec].low32 = low32;
awilliam@9745 421 iosapic_intr_info[vec].dest = dest;
awilliam@9745 422 list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
awilliam@9745 423 addr = rte->addr;
awilliam@9745 424 rte_index = rte->rte_index;
awilliam@9745 425 iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
awilliam@9745 426 iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
awilliam@9745 427 }
awilliam@9745 428 }
awilliam@9745 429 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 430 #endif
awilliam@9745 431 }
awilliam@9745 432
awilliam@9745 433 /*
awilliam@9745 434 * Handlers for level-triggered interrupts.
awilliam@9745 435 */
awilliam@9745 436
awilliam@9745 437 static unsigned int
awilliam@9745 438 iosapic_startup_level_irq (unsigned int irq)
awilliam@9745 439 {
awilliam@9745 440 unmask_irq(irq);
awilliam@9745 441 return 0;
awilliam@9745 442 }
awilliam@9745 443
awilliam@9745 444 static void
awilliam@9745 445 iosapic_end_level_irq (unsigned int irq)
awilliam@9745 446 {
awilliam@9745 447 ia64_vector vec = irq_to_vector(irq);
awilliam@9745 448 struct iosapic_rte_info *rte;
awilliam@9745 449
awilliam@9745 450 move_irq(irq);
awilliam@9745 451 list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
awilliam@9745 452 iosapic_eoi(rte->addr, vec);
awilliam@9745 453 }
awilliam@9745 454
awilliam@9745 455 #define iosapic_shutdown_level_irq mask_irq
awilliam@9745 456 #define iosapic_enable_level_irq unmask_irq
awilliam@9745 457 #define iosapic_disable_level_irq mask_irq
awilliam@9745 458 #define iosapic_ack_level_irq nop
awilliam@9745 459
awilliam@9745 460 struct hw_interrupt_type irq_type_iosapic_level = {
awilliam@9745 461 .typename = "IO-SAPIC-level",
awilliam@9745 462 .startup = iosapic_startup_level_irq,
awilliam@9745 463 .shutdown = iosapic_shutdown_level_irq,
awilliam@9745 464 .enable = iosapic_enable_level_irq,
awilliam@9745 465 .disable = iosapic_disable_level_irq,
awilliam@9745 466 .ack = iosapic_ack_level_irq,
awilliam@9745 467 .end = iosapic_end_level_irq,
awilliam@9745 468 .set_affinity = iosapic_set_affinity
awilliam@9745 469 };
awilliam@9745 470
awilliam@9745 471 /*
awilliam@9745 472 * Handlers for edge-triggered interrupts.
awilliam@9745 473 */
awilliam@9745 474
awilliam@9745 475 static unsigned int
awilliam@9745 476 iosapic_startup_edge_irq (unsigned int irq)
awilliam@9745 477 {
awilliam@9745 478 unmask_irq(irq);
awilliam@9745 479 /*
awilliam@9745 480 * IOSAPIC simply drops interrupts pended while the
awilliam@9745 481 * corresponding pin was masked, so we can't know if an
awilliam@9745 482 * interrupt is pending already. Let's hope not...
awilliam@9745 483 */
awilliam@9745 484 return 0;
awilliam@9745 485 }
awilliam@9745 486
awilliam@9745 487 static void
awilliam@9745 488 iosapic_ack_edge_irq (unsigned int irq)
awilliam@9745 489 {
awilliam@9745 490 irq_desc_t *idesc = irq_descp(irq);
awilliam@9745 491
awilliam@9745 492 move_irq(irq);
awilliam@9745 493 /*
awilliam@9745 494 * Once we have recorded IRQ_PENDING already, we can mask the
awilliam@9745 495 * interrupt for real. This prevents IRQ storms from unhandled
awilliam@9745 496 * devices.
awilliam@9745 497 */
awilliam@9745 498 if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED))
awilliam@9745 499 mask_irq(irq);
awilliam@9745 500 }
awilliam@9745 501
awilliam@9745 502 #define iosapic_enable_edge_irq unmask_irq
awilliam@9745 503 #define iosapic_disable_edge_irq nop
awilliam@9745 504 #define iosapic_end_edge_irq nop
awilliam@9745 505
awilliam@9745 506 struct hw_interrupt_type irq_type_iosapic_edge = {
awilliam@9745 507 .typename = "IO-SAPIC-edge",
awilliam@9745 508 .startup = iosapic_startup_edge_irq,
awilliam@9745 509 .shutdown = iosapic_disable_edge_irq,
awilliam@9745 510 .enable = iosapic_enable_edge_irq,
awilliam@9745 511 .disable = iosapic_disable_edge_irq,
awilliam@9745 512 .ack = iosapic_ack_edge_irq,
awilliam@9745 513 .end = iosapic_end_edge_irq,
awilliam@9745 514 .set_affinity = iosapic_set_affinity
awilliam@9745 515 };
awilliam@9745 516
awilliam@9745 517 unsigned int
awilliam@9745 518 iosapic_version (char __iomem *addr)
awilliam@9745 519 {
awilliam@9745 520 /*
awilliam@9745 521 * IOSAPIC Version Register return 32 bit structure like:
awilliam@9745 522 * {
awilliam@9745 523 * unsigned int version : 8;
awilliam@9745 524 * unsigned int reserved1 : 8;
awilliam@9745 525 * unsigned int max_redir : 8;
awilliam@9745 526 * unsigned int reserved2 : 8;
awilliam@9745 527 * }
awilliam@9745 528 */
awilliam@9745 529 return iosapic_read(addr, IOSAPIC_VERSION);
awilliam@9745 530 }
awilliam@9745 531
awilliam@9745 532 static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol)
awilliam@9745 533 {
awilliam@9745 534 int i, vector = -1, min_count = -1;
awilliam@9745 535 struct iosapic_intr_info *info;
awilliam@9745 536
awilliam@9745 537 /*
awilliam@9745 538 * shared vectors for edge-triggered interrupts are not
awilliam@9745 539 * supported yet
awilliam@9745 540 */
awilliam@9745 541 if (trigger == IOSAPIC_EDGE)
awilliam@9745 542 return -1;
awilliam@9745 543
awilliam@9745 544 for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) {
awilliam@9745 545 info = &iosapic_intr_info[i];
awilliam@9745 546 if (info->trigger == trigger && info->polarity == pol &&
awilliam@9745 547 (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) {
awilliam@9745 548 if (min_count == -1 || info->count < min_count) {
awilliam@9745 549 vector = i;
awilliam@9745 550 min_count = info->count;
awilliam@9745 551 }
awilliam@9745 552 }
awilliam@9745 553 }
awilliam@9745 554
awilliam@9745 555 return vector;
awilliam@9745 556 }
awilliam@9745 557
awilliam@9745 558 /*
awilliam@9745 559 * if the given vector is already owned by other,
awilliam@9745 560 * assign a new vector for the other and make the vector available
awilliam@9745 561 */
awilliam@9745 562 static void __init
awilliam@9745 563 iosapic_reassign_vector (int vector)
awilliam@9745 564 {
awilliam@9745 565 int new_vector;
awilliam@9745 566
awilliam@9745 567 if (!list_empty(&iosapic_intr_info[vector].rtes)) {
awilliam@9745 568 new_vector = assign_irq_vector(AUTO_ASSIGN);
awilliam@9745 569 if (new_vector < 0)
awilliam@9745 570 panic("%s: out of interrupt vectors!\n", __FUNCTION__);
awilliam@9745 571 printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
awilliam@9745 572 memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
awilliam@9745 573 sizeof(struct iosapic_intr_info));
awilliam@9745 574 INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes);
awilliam@9745 575 list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes);
awilliam@9745 576 memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
awilliam@9745 577 iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
awilliam@9745 578 INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);
awilliam@9745 579 }
awilliam@9745 580 }
awilliam@9745 581
awilliam@9745 582 static struct iosapic_rte_info *iosapic_alloc_rte (void)
awilliam@9745 583 {
awilliam@9745 584 int i;
awilliam@9745 585 struct iosapic_rte_info *rte;
awilliam@9745 586 int preallocated = 0;
awilliam@9745 587
awilliam@9745 588 if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) {
awilliam@9745 589 rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);
awilliam@9745 590 if (!rte)
awilliam@9745 591 return NULL;
awilliam@9745 592 for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++)
awilliam@9745 593 list_add(&rte->rte_list, &free_rte_list);
awilliam@9745 594 }
awilliam@9745 595
awilliam@9745 596 if (!list_empty(&free_rte_list)) {
awilliam@9745 597 rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list);
awilliam@9745 598 list_del(&rte->rte_list);
awilliam@9745 599 preallocated++;
awilliam@9745 600 } else {
awilliam@9745 601 rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC);
awilliam@9745 602 if (!rte)
awilliam@9745 603 return NULL;
awilliam@9745 604 }
awilliam@9745 605
awilliam@9745 606 memset(rte, 0, sizeof(struct iosapic_rte_info));
awilliam@9745 607 if (preallocated)
awilliam@9745 608 rte->flags |= RTE_PREALLOCATED;
awilliam@9745 609
awilliam@9745 610 return rte;
awilliam@9745 611 }
awilliam@9745 612
awilliam@9745 613 static void iosapic_free_rte (struct iosapic_rte_info *rte)
awilliam@9745 614 {
awilliam@9745 615 if (rte->flags & RTE_PREALLOCATED)
awilliam@9745 616 list_add_tail(&rte->rte_list, &free_rte_list);
awilliam@9745 617 else
awilliam@9745 618 kfree(rte);
awilliam@9745 619 }
awilliam@9745 620
awilliam@9745 621 static inline int vector_is_shared (int vector)
awilliam@9745 622 {
awilliam@9745 623 return (iosapic_intr_info[vector].count > 1);
awilliam@9745 624 }
awilliam@9745 625
awilliam@9745 626 static int
awilliam@9745 627 register_intr (unsigned int gsi, int vector, unsigned char delivery,
awilliam@9745 628 unsigned long polarity, unsigned long trigger)
awilliam@9745 629 {
awilliam@9745 630 irq_desc_t *idesc;
awilliam@9745 631 struct hw_interrupt_type *irq_type;
awilliam@9745 632 int rte_index;
awilliam@9745 633 int index;
awilliam@9745 634 unsigned long gsi_base;
awilliam@9745 635 void __iomem *iosapic_address;
awilliam@9745 636 struct iosapic_rte_info *rte;
awilliam@9745 637
awilliam@9745 638 index = find_iosapic(gsi);
awilliam@9745 639 if (index < 0) {
awilliam@9745 640 printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi);
awilliam@9745 641 return -ENODEV;
awilliam@9745 642 }
awilliam@9745 643
awilliam@9745 644 iosapic_address = iosapic_lists[index].addr;
awilliam@9745 645 gsi_base = iosapic_lists[index].gsi_base;
awilliam@9745 646
awilliam@9745 647 rte = gsi_vector_to_rte(gsi, vector);
awilliam@9745 648 if (!rte) {
awilliam@9745 649 rte = iosapic_alloc_rte();
awilliam@9745 650 if (!rte) {
awilliam@9745 651 printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__);
awilliam@9745 652 return -ENOMEM;
awilliam@9745 653 }
awilliam@9745 654
awilliam@9745 655 rte_index = gsi - gsi_base;
awilliam@9745 656 rte->rte_index = rte_index;
awilliam@9745 657 rte->addr = iosapic_address;
awilliam@9745 658 rte->gsi_base = gsi_base;
awilliam@9745 659 rte->refcnt++;
awilliam@9745 660 list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes);
awilliam@9745 661 iosapic_intr_info[vector].count++;
awilliam@9745 662 iosapic_lists[index].rtes_inuse++;
awilliam@9745 663 }
awilliam@9745 664 else if (vector_is_shared(vector)) {
awilliam@9745 665 struct iosapic_intr_info *info = &iosapic_intr_info[vector];
awilliam@9745 666 if (info->trigger != trigger || info->polarity != polarity) {
awilliam@9745 667 printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__);
awilliam@9745 668 return -EINVAL;
awilliam@9745 669 }
awilliam@9745 670 }
awilliam@9745 671
awilliam@9745 672 iosapic_intr_info[vector].polarity = polarity;
awilliam@9745 673 iosapic_intr_info[vector].dmode = delivery;
awilliam@9745 674 iosapic_intr_info[vector].trigger = trigger;
awilliam@9745 675
awilliam@9745 676 if (trigger == IOSAPIC_EDGE)
awilliam@9745 677 irq_type = &irq_type_iosapic_edge;
awilliam@9745 678 else
awilliam@9745 679 irq_type = &irq_type_iosapic_level;
awilliam@9745 680
awilliam@9745 681 idesc = irq_descp(vector);
awilliam@9745 682 if (idesc->handler != irq_type) {
awilliam@9745 683 if (idesc->handler != &no_irq_type)
awilliam@9745 684 printk(KERN_WARNING "%s: changing vector %d from %s to %s\n",
awilliam@9745 685 __FUNCTION__, vector, idesc->handler->typename, irq_type->typename);
awilliam@9745 686 idesc->handler = irq_type;
awilliam@9745 687 }
awilliam@9745 688 return 0;
awilliam@9745 689 }
awilliam@9745 690
awilliam@9745 691 static unsigned int
awilliam@9745 692 get_target_cpu (unsigned int gsi, int vector)
awilliam@9745 693 {
awilliam@9745 694 #ifdef CONFIG_SMP
awilliam@9745 695 static int cpu = -1;
awilliam@9745 696
awilliam@9745 697 /*
awilliam@9745 698 * In case of vector shared by multiple RTEs, all RTEs that
awilliam@9745 699 * share the vector need to use the same destination CPU.
awilliam@9745 700 */
awilliam@9745 701 if (!list_empty(&iosapic_intr_info[vector].rtes))
awilliam@9745 702 return iosapic_intr_info[vector].dest;
awilliam@9745 703
awilliam@9745 704 /*
awilliam@9745 705 * If the platform supports redirection via XTP, let it
awilliam@9745 706 * distribute interrupts.
awilliam@9745 707 */
awilliam@9745 708 if (smp_int_redirect & SMP_IRQ_REDIRECTION)
awilliam@9745 709 return cpu_physical_id(smp_processor_id());
awilliam@9745 710
awilliam@9745 711 /*
awilliam@9745 712 * Some interrupts (ACPI SCI, for instance) are registered
awilliam@9745 713 * before the BSP is marked as online.
awilliam@9745 714 */
awilliam@9745 715 if (!cpu_online(smp_processor_id()))
awilliam@9745 716 return cpu_physical_id(smp_processor_id());
awilliam@9745 717
awilliam@9745 718 #ifdef CONFIG_NUMA
awilliam@9745 719 {
awilliam@9745 720 int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0;
awilliam@9745 721 cpumask_t cpu_mask;
awilliam@9745 722
awilliam@9745 723 iosapic_index = find_iosapic(gsi);
awilliam@9745 724 if (iosapic_index < 0 ||
awilliam@9745 725 iosapic_lists[iosapic_index].node == MAX_NUMNODES)
awilliam@9745 726 goto skip_numa_setup;
awilliam@9745 727
awilliam@9745 728 cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node);
awilliam@9745 729
awilliam@9745 730 for_each_cpu_mask(numa_cpu, cpu_mask) {
awilliam@9745 731 if (!cpu_online(numa_cpu))
awilliam@9745 732 cpu_clear(numa_cpu, cpu_mask);
awilliam@9745 733 }
awilliam@9745 734
awilliam@9745 735 num_cpus = cpus_weight(cpu_mask);
awilliam@9745 736
awilliam@9745 737 if (!num_cpus)
awilliam@9745 738 goto skip_numa_setup;
awilliam@9745 739
awilliam@9745 740 /* Use vector assigment to distribute across cpus in node */
awilliam@9745 741 cpu_index = vector % num_cpus;
awilliam@9745 742
awilliam@9745 743 for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++)
awilliam@9745 744 numa_cpu = next_cpu(numa_cpu, cpu_mask);
awilliam@9745 745
awilliam@9745 746 if (numa_cpu != NR_CPUS)
awilliam@9745 747 return cpu_physical_id(numa_cpu);
awilliam@9745 748 }
awilliam@9745 749 skip_numa_setup:
awilliam@9745 750 #endif
awilliam@9745 751 /*
awilliam@9745 752 * Otherwise, round-robin interrupt vectors across all the
awilliam@9745 753 * processors. (It'd be nice if we could be smarter in the
awilliam@9745 754 * case of NUMA.)
awilliam@9745 755 */
awilliam@9745 756 do {
awilliam@9745 757 if (++cpu >= NR_CPUS)
awilliam@9745 758 cpu = 0;
awilliam@9745 759 } while (!cpu_online(cpu));
awilliam@9745 760
awilliam@9745 761 return cpu_physical_id(cpu);
awilliam@9745 762 #else
awilliam@9745 763 return cpu_physical_id(smp_processor_id());
awilliam@9745 764 #endif
awilliam@9745 765 }
awilliam@9745 766
awilliam@9745 767 /*
awilliam@9745 768 * ACPI can describe IOSAPIC interrupts via static tables and namespace
awilliam@9745 769 * methods. This provides an interface to register those interrupts and
awilliam@9745 770 * program the IOSAPIC RTE.
awilliam@9745 771 */
awilliam@9745 772 int
awilliam@9745 773 iosapic_register_intr (unsigned int gsi,
awilliam@9745 774 unsigned long polarity, unsigned long trigger)
awilliam@9745 775 {
awilliam@9745 776 int vector, mask = 1, err;
awilliam@9745 777 unsigned int dest;
awilliam@9745 778 unsigned long flags;
awilliam@9745 779 struct iosapic_rte_info *rte;
awilliam@9745 780 u32 low32;
awilliam@9745 781 again:
awilliam@9745 782 /*
awilliam@9745 783 * If this GSI has already been registered (i.e., it's a
awilliam@9745 784 * shared interrupt, or we lost a race to register it),
awilliam@9745 785 * don't touch the RTE.
awilliam@9745 786 */
awilliam@9745 787 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 788 {
awilliam@9745 789 vector = gsi_to_vector(gsi);
awilliam@9745 790 if (vector > 0) {
awilliam@9745 791 rte = gsi_vector_to_rte(gsi, vector);
awilliam@9745 792 rte->refcnt++;
awilliam@9745 793 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 794 return vector;
awilliam@9745 795 }
awilliam@9745 796 }
awilliam@9745 797 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 798
awilliam@9745 799 /* If vector is running out, we try to find a sharable vector */
awilliam@9745 800 vector = assign_irq_vector(AUTO_ASSIGN);
awilliam@9745 801 if (vector < 0) {
awilliam@9745 802 vector = iosapic_find_sharable_vector(trigger, polarity);
awilliam@9745 803 if (vector < 0)
awilliam@9745 804 return -ENOSPC;
awilliam@9745 805 }
awilliam@9745 806
awilliam@9745 807 spin_lock_irqsave(&irq_descp(vector)->lock, flags);
awilliam@9745 808 spin_lock(&iosapic_lock);
awilliam@9745 809 {
awilliam@9745 810 if (gsi_to_vector(gsi) > 0) {
awilliam@9745 811 if (list_empty(&iosapic_intr_info[vector].rtes))
awilliam@9745 812 free_irq_vector(vector);
awilliam@9745 813 spin_unlock(&iosapic_lock);
awilliam@9745 814 spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
awilliam@9745 815 goto again;
awilliam@9745 816 }
awilliam@9745 817
awilliam@9745 818 dest = get_target_cpu(gsi, vector);
awilliam@9745 819 err = register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
awilliam@9745 820 polarity, trigger);
awilliam@9745 821 if (err < 0) {
awilliam@9745 822 spin_unlock(&iosapic_lock);
awilliam@9745 823 spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
awilliam@9745 824 return err;
awilliam@9745 825 }
awilliam@9745 826
awilliam@9745 827 /*
awilliam@9745 828 * If the vector is shared and already unmasked for
awilliam@9745 829 * other interrupt sources, don't mask it.
awilliam@9745 830 */
awilliam@9745 831 low32 = iosapic_intr_info[vector].low32;
awilliam@9745 832 if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK))
awilliam@9745 833 mask = 0;
awilliam@9745 834 set_rte(gsi, vector, dest, mask);
awilliam@9745 835 }
awilliam@9745 836 spin_unlock(&iosapic_lock);
awilliam@9745 837 spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
awilliam@9745 838
awilliam@9745 839 printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
awilliam@9745 840 gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
awilliam@9745 841 (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
awilliam@9745 842 cpu_logical_id(dest), dest, vector);
awilliam@9745 843
awilliam@9745 844 return vector;
awilliam@9745 845 }
awilliam@9745 846
awilliam@9745 847 void
awilliam@9745 848 iosapic_unregister_intr (unsigned int gsi)
awilliam@9745 849 {
awilliam@9745 850 unsigned long flags;
awilliam@9745 851 int irq, vector, index;
awilliam@9745 852 irq_desc_t *idesc;
awilliam@9745 853 u32 low32;
awilliam@9745 854 unsigned long trigger, polarity;
awilliam@9745 855 unsigned int dest;
awilliam@9745 856 struct iosapic_rte_info *rte;
awilliam@9745 857
awilliam@9745 858 /*
awilliam@9745 859 * If the irq associated with the gsi is not found,
awilliam@9745 860 * iosapic_unregister_intr() is unbalanced. We need to check
awilliam@9745 861 * this again after getting locks.
awilliam@9745 862 */
awilliam@9745 863 irq = gsi_to_irq(gsi);
awilliam@9745 864 if (irq < 0) {
awilliam@9745 865 printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
awilliam@9745 866 WARN_ON(1);
awilliam@9745 867 return;
awilliam@9745 868 }
awilliam@9745 869 vector = irq_to_vector(irq);
awilliam@9745 870
awilliam@9745 871 idesc = irq_descp(irq);
awilliam@9745 872 spin_lock_irqsave(&idesc->lock, flags);
awilliam@9745 873 spin_lock(&iosapic_lock);
awilliam@9745 874 {
awilliam@9745 875 if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) {
awilliam@9745 876 printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
awilliam@9745 877 WARN_ON(1);
awilliam@9745 878 goto out;
awilliam@9745 879 }
awilliam@9745 880
awilliam@9745 881 if (--rte->refcnt > 0)
awilliam@9745 882 goto out;
awilliam@9745 883
awilliam@9745 884 /* Mask the interrupt */
awilliam@9745 885 low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK;
awilliam@9745 886 iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32);
awilliam@9745 887
awilliam@9745 888 /* Remove the rte entry from the list */
awilliam@9745 889 list_del(&rte->rte_list);
awilliam@9745 890 iosapic_intr_info[vector].count--;
awilliam@9745 891 iosapic_free_rte(rte);
awilliam@9745 892 index = find_iosapic(gsi);
awilliam@9745 893 iosapic_lists[index].rtes_inuse--;
awilliam@9745 894 WARN_ON(iosapic_lists[index].rtes_inuse < 0);
awilliam@9745 895
awilliam@9745 896 trigger = iosapic_intr_info[vector].trigger;
awilliam@9745 897 polarity = iosapic_intr_info[vector].polarity;
awilliam@9745 898 dest = iosapic_intr_info[vector].dest;
awilliam@9745 899 printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n",
awilliam@9745 900 gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
awilliam@9745 901 (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
awilliam@9745 902 cpu_logical_id(dest), dest, vector);
awilliam@9745 903
awilliam@9745 904 if (list_empty(&iosapic_intr_info[vector].rtes)) {
awilliam@9745 905 /* Sanity check */
awilliam@9745 906 BUG_ON(iosapic_intr_info[vector].count);
awilliam@9745 907
awilliam@9745 908 /* Clear the interrupt controller descriptor */
awilliam@9745 909 idesc->handler = &no_irq_type;
awilliam@9745 910
awilliam@9745 911 /* Clear the interrupt information */
awilliam@9745 912 memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
awilliam@9745 913 iosapic_intr_info[vector].low32 |= IOSAPIC_MASK;
awilliam@9745 914 INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);
awilliam@9745 915
awilliam@9745 916 if (idesc->action) {
awilliam@9745 917 printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq);
awilliam@9745 918 WARN_ON(1);
awilliam@9745 919 }
awilliam@9745 920
awilliam@9745 921 /* Free the interrupt vector */
awilliam@9745 922 free_irq_vector(vector);
awilliam@9745 923 }
awilliam@9745 924 }
awilliam@9745 925 out:
awilliam@9745 926 spin_unlock(&iosapic_lock);
awilliam@9745 927 spin_unlock_irqrestore(&idesc->lock, flags);
awilliam@9745 928 }
awilliam@9745 929
awilliam@9745 930 /*
awilliam@9745 931 * ACPI calls this when it finds an entry for a platform interrupt.
awilliam@9745 932 * Note that the irq_base and IOSAPIC address must be set in iosapic_init().
awilliam@9745 933 */
awilliam@9745 934 int __init
awilliam@9745 935 iosapic_register_platform_intr (u32 int_type, unsigned int gsi,
awilliam@9745 936 int iosapic_vector, u16 eid, u16 id,
awilliam@9745 937 unsigned long polarity, unsigned long trigger)
awilliam@9745 938 {
awilliam@9745 939 static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"};
awilliam@9745 940 unsigned char delivery;
awilliam@9745 941 int vector, mask = 0;
awilliam@9745 942 unsigned int dest = ((id << 8) | eid) & 0xffff;
awilliam@9745 943
awilliam@9745 944 switch (int_type) {
awilliam@9745 945 case ACPI_INTERRUPT_PMI:
awilliam@9745 946 vector = iosapic_vector;
awilliam@9745 947 /*
awilliam@9745 948 * since PMI vector is alloc'd by FW(ACPI) not by kernel,
awilliam@9745 949 * we need to make sure the vector is available
awilliam@9745 950 */
awilliam@9745 951 iosapic_reassign_vector(vector);
awilliam@9745 952 delivery = IOSAPIC_PMI;
awilliam@9745 953 break;
awilliam@9745 954 case ACPI_INTERRUPT_INIT:
awilliam@9745 955 vector = assign_irq_vector(AUTO_ASSIGN);
awilliam@9745 956 if (vector < 0)
awilliam@9745 957 panic("%s: out of interrupt vectors!\n", __FUNCTION__);
awilliam@9745 958 delivery = IOSAPIC_INIT;
awilliam@9745 959 break;
awilliam@9745 960 case ACPI_INTERRUPT_CPEI:
awilliam@9745 961 vector = IA64_CPE_VECTOR;
awilliam@9745 962 delivery = IOSAPIC_LOWEST_PRIORITY;
awilliam@9745 963 mask = 1;
awilliam@9745 964 break;
awilliam@9745 965 default:
awilliam@9745 966 printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type);
awilliam@9745 967 return -1;
awilliam@9745 968 }
awilliam@9745 969
awilliam@9745 970 register_intr(gsi, vector, delivery, polarity, trigger);
awilliam@9745 971
awilliam@9745 972 printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
awilliam@9745 973 int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown",
awilliam@9745 974 int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
awilliam@9745 975 (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
awilliam@9745 976 cpu_logical_id(dest), dest, vector);
awilliam@9745 977
awilliam@9745 978 set_rte(gsi, vector, dest, mask);
awilliam@9745 979 return vector;
awilliam@9745 980 }
awilliam@9745 981
awilliam@9745 982
awilliam@9745 983 /*
awilliam@9745 984 * ACPI calls this when it finds an entry for a legacy ISA IRQ override.
awilliam@9745 985 * Note that the gsi_base and IOSAPIC address must be set in iosapic_init().
awilliam@9745 986 */
awilliam@9745 987 void __init
awilliam@9745 988 iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
awilliam@9745 989 unsigned long polarity,
awilliam@9745 990 unsigned long trigger)
awilliam@9745 991 {
awilliam@9745 992 int vector;
awilliam@9745 993 unsigned int dest = cpu_physical_id(smp_processor_id());
awilliam@9745 994
awilliam@9745 995 vector = isa_irq_to_vector(isa_irq);
awilliam@9745 996
awilliam@9745 997 register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);
awilliam@9745 998
awilliam@9745 999 DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n",
awilliam@9745 1000 isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level",
awilliam@9745 1001 polarity == IOSAPIC_POL_HIGH ? "high" : "low",
awilliam@9745 1002 cpu_logical_id(dest), dest, vector);
awilliam@9745 1003
awilliam@9745 1004 set_rte(gsi, vector, dest, 1);
awilliam@9745 1005 }
awilliam@9745 1006
awilliam@9745 1007 void __init
awilliam@9745 1008 iosapic_system_init (int system_pcat_compat)
awilliam@9745 1009 {
awilliam@9745 1010 int vector;
awilliam@9745 1011
awilliam@9745 1012 for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) {
awilliam@9745 1013 iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
awilliam@9745 1014 INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); /* mark as unused */
awilliam@9745 1015 }
awilliam@9745 1016
awilliam@9745 1017 pcat_compat = system_pcat_compat;
awilliam@9747 1018 #ifdef CONFIG_XEN
awilliam@9747 1019 if (running_on_xen)
awilliam@9747 1020 return;
awilliam@9747 1021 #endif
awilliam@9745 1022 if (pcat_compat) {
awilliam@9745 1023 /*
awilliam@9745 1024 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
awilliam@9745 1025 * enabled.
awilliam@9745 1026 */
awilliam@9745 1027 printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
awilliam@9745 1028 outb(0xff, 0xA1);
awilliam@9745 1029 outb(0xff, 0x21);
awilliam@9745 1030 }
awilliam@9745 1031 }
awilliam@9745 1032
awilliam@9745 1033 static inline int
awilliam@9745 1034 iosapic_alloc (void)
awilliam@9745 1035 {
awilliam@9745 1036 int index;
awilliam@9745 1037
awilliam@9745 1038 for (index = 0; index < NR_IOSAPICS; index++)
awilliam@9745 1039 if (!iosapic_lists[index].addr)
awilliam@9745 1040 return index;
awilliam@9745 1041
awilliam@9745 1042 printk(KERN_WARNING "%s: failed to allocate iosapic\n", __FUNCTION__);
awilliam@9745 1043 return -1;
awilliam@9745 1044 }
awilliam@9745 1045
awilliam@9745 1046 static inline void
awilliam@9745 1047 iosapic_free (int index)
awilliam@9745 1048 {
awilliam@9745 1049 memset(&iosapic_lists[index], 0, sizeof(iosapic_lists[0]));
awilliam@9745 1050 }
awilliam@9745 1051
awilliam@9745 1052 static inline int
awilliam@9745 1053 iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver)
awilliam@9745 1054 {
awilliam@9745 1055 int index;
awilliam@9745 1056 unsigned int gsi_end, base, end;
awilliam@9745 1057
awilliam@9745 1058 /* check gsi range */
awilliam@9745 1059 gsi_end = gsi_base + ((ver >> 16) & 0xff);
awilliam@9745 1060 for (index = 0; index < NR_IOSAPICS; index++) {
awilliam@9745 1061 if (!iosapic_lists[index].addr)
awilliam@9745 1062 continue;
awilliam@9745 1063
awilliam@9745 1064 base = iosapic_lists[index].gsi_base;
awilliam@9745 1065 end = base + iosapic_lists[index].num_rte - 1;
awilliam@9745 1066
awilliam@9745 1067 if (gsi_base < base && gsi_end < base)
awilliam@9745 1068 continue;/* OK */
awilliam@9745 1069
awilliam@9745 1070 if (gsi_base > end && gsi_end > end)
awilliam@9745 1071 continue; /* OK */
awilliam@9745 1072
awilliam@9745 1073 return -EBUSY;
awilliam@9745 1074 }
awilliam@9745 1075 return 0;
awilliam@9745 1076 }
awilliam@9745 1077
awilliam@9745 1078 int __devinit
awilliam@9745 1079 iosapic_init (unsigned long phys_addr, unsigned int gsi_base)
awilliam@9745 1080 {
awilliam@9745 1081 int num_rte, err, index;
awilliam@9745 1082 unsigned int isa_irq, ver;
awilliam@9745 1083 char __iomem *addr;
awilliam@9745 1084 unsigned long flags;
awilliam@9745 1085
awilliam@9745 1086 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 1087 {
awilliam@9745 1088 addr = ioremap(phys_addr, 0);
awilliam@9745 1089 ver = iosapic_version(addr);
awilliam@9745 1090
awilliam@9745 1091 if ((err = iosapic_check_gsi_range(gsi_base, ver))) {
awilliam@9745 1092 iounmap(addr);
awilliam@9745 1093 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 1094 return err;
awilliam@9745 1095 }
awilliam@9745 1096
awilliam@9745 1097 /*
awilliam@9745 1098 * The MAX_REDIR register holds the highest input pin
awilliam@9745 1099 * number (starting from 0).
awilliam@9745 1100 * We add 1 so that we can use it for number of pins (= RTEs)
awilliam@9745 1101 */
awilliam@9745 1102 num_rte = ((ver >> 16) & 0xff) + 1;
awilliam@9745 1103
awilliam@9745 1104 index = iosapic_alloc();
awilliam@9745 1105 iosapic_lists[index].addr = addr;
awilliam@9745 1106 iosapic_lists[index].gsi_base = gsi_base;
awilliam@9745 1107 iosapic_lists[index].num_rte = num_rte;
awilliam@9745 1108 #ifdef CONFIG_NUMA
awilliam@9745 1109 iosapic_lists[index].node = MAX_NUMNODES;
awilliam@9745 1110 #endif
awilliam@9745 1111 }
awilliam@9745 1112 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 1113
awilliam@9745 1114 if ((gsi_base == 0) && pcat_compat) {
awilliam@9745 1115 /*
awilliam@9745 1116 * Map the legacy ISA devices into the IOSAPIC data. Some of these may
awilliam@9745 1117 * get reprogrammed later on with data from the ACPI Interrupt Source
awilliam@9745 1118 * Override table.
awilliam@9745 1119 */
awilliam@9745 1120 for (isa_irq = 0; isa_irq < 16; ++isa_irq)
awilliam@9745 1121 iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE);
awilliam@9745 1122 }
awilliam@9745 1123 return 0;
awilliam@9745 1124 }
awilliam@9745 1125
awilliam@9745 1126 #ifdef CONFIG_HOTPLUG
awilliam@9745 1127 int
awilliam@9745 1128 iosapic_remove (unsigned int gsi_base)
awilliam@9745 1129 {
awilliam@9745 1130 int index, err = 0;
awilliam@9745 1131 unsigned long flags;
awilliam@9745 1132
awilliam@9745 1133 spin_lock_irqsave(&iosapic_lock, flags);
awilliam@9745 1134 {
awilliam@9745 1135 index = find_iosapic(gsi_base);
awilliam@9745 1136 if (index < 0) {
awilliam@9745 1137 printk(KERN_WARNING "%s: No IOSAPIC for GSI base %u\n",
awilliam@9745 1138 __FUNCTION__, gsi_base);
awilliam@9745 1139 goto out;
awilliam@9745 1140 }
awilliam@9745 1141
awilliam@9745 1142 if (iosapic_lists[index].rtes_inuse) {
awilliam@9745 1143 err = -EBUSY;
awilliam@9745 1144 printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n",
awilliam@9745 1145 __FUNCTION__, gsi_base);
awilliam@9745 1146 goto out;
awilliam@9745 1147 }
awilliam@9745 1148
awilliam@9745 1149 iounmap(iosapic_lists[index].addr);
awilliam@9745 1150 iosapic_free(index);
awilliam@9745 1151 }
awilliam@9745 1152 out:
awilliam@9745 1153 spin_unlock_irqrestore(&iosapic_lock, flags);
awilliam@9745 1154 return err;
awilliam@9745 1155 }
awilliam@9745 1156 #endif /* CONFIG_HOTPLUG */
awilliam@9745 1157
awilliam@9745 1158 #ifdef CONFIG_NUMA
awilliam@9745 1159 void __devinit
awilliam@9745 1160 map_iosapic_to_node(unsigned int gsi_base, int node)
awilliam@9745 1161 {
awilliam@9745 1162 int index;
awilliam@9745 1163
awilliam@9745 1164 index = find_iosapic(gsi_base);
awilliam@9745 1165 if (index < 0) {
awilliam@9745 1166 printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n",
awilliam@9745 1167 __FUNCTION__, gsi_base);
awilliam@9745 1168 return;
awilliam@9745 1169 }
awilliam@9745 1170 iosapic_lists[index].node = node;
awilliam@9745 1171 return;
awilliam@9745 1172 }
awilliam@9745 1173 #endif
awilliam@9745 1174
awilliam@9745 1175 static int __init iosapic_enable_kmalloc (void)
awilliam@9745 1176 {
awilliam@9745 1177 iosapic_kmalloc_ok = 1;
awilliam@9745 1178 return 0;
awilliam@9745 1179 }
awilliam@9745 1180 core_initcall (iosapic_enable_kmalloc);