ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / kernel / iosapic.c
1 /*
2  * I/O SAPIC support.
3  *
4  * Copyright (C) 1999 Intel Corp.
5  * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com>
6  * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com>
7  * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co.
8  *      David Mosberger-Tang <davidm@hpl.hp.com>
9  * Copyright (C) 1999 VA Linux Systems
10  * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
11  *
12  * 00/04/19     D. Mosberger    Rewritten to mirror more closely the x86 I/O APIC code.
13  *                              In particular, we now have separate handlers for edge
14  *                              and level triggered interrupts.
15  * 00/10/27     Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation
16  *                              PCI to vector mapping, shared PCI interrupts.
17  * 00/10/27     D. Mosberger    Document things a bit more to make them more understandable.
18  *                              Clean up much of the old IOSAPIC cruft.
19  * 01/07/27     J.I. Lee        PCI irq routing, Platform/Legacy interrupts and fixes for
20  *                              ACPI S5(SoftOff) support.
21  * 02/01/23     J.I. Lee        iosapic pgm fixes for PCI irq routing from _PRT
22  * 02/01/07     E. Focht        <efocht@ess.nec.de> Redirectable interrupt vectors in
23  *                              iosapic_set_affinity(), initializations for
24  *                              /proc/irq/#/smp_affinity
25  * 02/04/02     P. Diefenbaugh  Cleaned up ACPI PCI IRQ routing.
26  * 02/04/18     J.I. Lee        bug fix in iosapic_init_pci_irq
27  * 02/04/30     J.I. Lee        bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping
28  *                              error
29  * 02/07/29     T. Kochi        Allocate interrupt vectors dynamically
30  * 02/08/04     T. Kochi        Cleaned up terminology (irq, global system interrupt, vector, etc.)
31  * 02/09/20     D. Mosberger    Simplified by taking advantage of ACPI's pci_irq code.
32  * 03/02/19     B. Helgaas      Make pcat_compat system-wide, not per-IOSAPIC.
33  *                              Remove iosapic_address & gsi_base from external interfaces.
34  *                              Rationalize __init/__devinit attributes.
35  */
36 /*
37  * Here is what the interrupt logic between a PCI device and the kernel looks like:
38  *
39  * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD).  The
40  *     device is uniquely identified by its bus--, and slot-number (the function
41  *     number does not matter here because all functions share the same interrupt
42  *     lines).
43  *
44  * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller.
45  *     Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level
46  *     triggered and use the same polarity).  Each interrupt line has a unique Global
47  *     System Interrupt (GSI) number which can be calculated as the sum of the controller's
48  *     base GSI number and the IOSAPIC pin number to which the line connects.
49  *
50  * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin
51  *     into the IA-64 interrupt vector.  This interrupt vector is then sent to the CPU.
52  *
53  * (4) The kernel recognizes an interrupt as an IRQ.  The IRQ interface is used as
54  *     architecture-independent interrupt handling mechanism in Linux.  As an
55  *     IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number
56  *     mapping.  On smaller systems, we use one-to-one mapping between IA-64 vector and
57  *     IRQ.  A platform can implement platform_irq_to_vector(irq) and
58  *     platform_local_vector_to_irq(vector) APIs to differentiate the mapping.
59  *     Please see also include/asm-ia64/hw_irq.h for those APIs.
60  *
61  * To sum up, there are three levels of mappings involved:
62  *
63  *      PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ
64  *
65  * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts.
66  * Now we use "IRQ" only for Linux IRQ's.  ISA IRQ (isa_irq) is the only exception in this
67  * source code.
68  */
69 #include <linux/config.h>
70
71 #include <linux/acpi.h>
72 #include <linux/init.h>
73 #include <linux/irq.h>
74 #include <linux/kernel.h>
75 #include <linux/list.h>
76 #include <linux/pci.h>
77 #include <linux/smp.h>
78 #include <linux/smp_lock.h>
79 #include <linux/string.h>
80
81 #include <asm/delay.h>
82 #include <asm/hw_irq.h>
83 #include <asm/io.h>
84 #include <asm/iosapic.h>
85 #include <asm/machvec.h>
86 #include <asm/processor.h>
87 #include <asm/ptrace.h>
88 #include <asm/system.h>
89
90
91 #undef DEBUG_INTERRUPT_ROUTING
92 #undef OVERRIDE_DEBUG
93
94 #ifdef DEBUG_INTERRUPT_ROUTING
95 #define DBG(fmt...)     printk(fmt)
96 #else
97 #define DBG(fmt...)
98 #endif
99
100 static spinlock_t iosapic_lock = SPIN_LOCK_UNLOCKED;
101
102 /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */
103
104 static struct iosapic_intr_info {
105         char            *addr;          /* base address of IOSAPIC */
106         u32             low32;          /* current value of low word of Redirection table entry */
107         unsigned int    gsi_base;       /* first GSI assigned to this IOSAPIC */
108         char            rte_index;      /* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */
109         unsigned char   dmode   : 3;    /* delivery mode (see iosapic.h) */
110         unsigned char   polarity: 1;    /* interrupt polarity (see iosapic.h) */
111         unsigned char   trigger : 1;    /* trigger mode (see iosapic.h) */
112 } iosapic_intr_info[IA64_NUM_VECTORS];
113
114 static struct iosapic {
115         char            *addr;          /* base address of IOSAPIC */
116         unsigned int    gsi_base;       /* first GSI assigned to this IOSAPIC */
117         unsigned short  num_rte;        /* number of RTE in this IOSAPIC */
118 } iosapic_lists[NR_IOSAPICS];
119
120 static int num_iosapic;
121
122 static unsigned char pcat_compat __initdata;    /* 8259 compatibility flag */
123
124
125 /*
126  * Find an IOSAPIC associated with a GSI
127  */
128 static inline int
129 find_iosapic (unsigned int gsi)
130 {
131         int i;
132
133         for (i = 0; i < num_iosapic; i++) {
134                 if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte)
135                         return i;
136         }
137
138         return -1;
139 }
140
141 static inline int
142 _gsi_to_vector (unsigned int gsi)
143 {
144         struct iosapic_intr_info *info;
145
146         for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)
147                 if (info->gsi_base + info->rte_index == gsi)
148                         return info - iosapic_intr_info;
149         return -1;
150 }
151
152 /*
153  * Translate GSI number to the corresponding IA-64 interrupt vector.  If no
154  * entry exists, return -1.
155  */
156 inline int
157 gsi_to_vector (unsigned int gsi)
158 {
159         return _gsi_to_vector(gsi);
160 }
161
162 int
163 gsi_to_irq (unsigned int gsi)
164 {
165         /*
166          * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq
167          * numbers...
168          */
169         return _gsi_to_vector(gsi);
170 }
171
172 static void
173 set_rte (unsigned int vector, unsigned int dest, int mask)
174 {
175         unsigned long pol, trigger, dmode, flags;
176         u32 low32, high32;
177         char *addr;
178         int rte_index;
179         char redir;
180
181         DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);
182
183         rte_index = iosapic_intr_info[vector].rte_index;
184         if (rte_index < 0)
185                 return;         /* not an IOSAPIC interrupt */
186
187         addr    = iosapic_intr_info[vector].addr;
188         pol     = iosapic_intr_info[vector].polarity;
189         trigger = iosapic_intr_info[vector].trigger;
190         dmode   = iosapic_intr_info[vector].dmode;
191
192         redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;
193 #ifdef CONFIG_SMP
194         {
195                 unsigned int irq;
196
197                 for (irq = 0; irq < NR_IRQS; ++irq)
198                         if (irq_to_vector(irq) == vector) {
199                                 set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);
200                                 break;
201                         }
202         }
203 #endif
204
205         low32 = ((pol << IOSAPIC_POLARITY_SHIFT) |
206                  (trigger << IOSAPIC_TRIGGER_SHIFT) |
207                  (dmode << IOSAPIC_DELIVERY_SHIFT) |
208                  ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) |
209                  vector);
210
211         /* dest contains both id and eid */
212         high32 = (dest << IOSAPIC_DEST_SHIFT);
213
214         spin_lock_irqsave(&iosapic_lock, flags);
215         {
216                 writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT);
217                 writel(high32, addr + IOSAPIC_WINDOW);
218                 writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
219                 writel(low32, addr + IOSAPIC_WINDOW);
220                 iosapic_intr_info[vector].low32 = low32;
221         }
222         spin_unlock_irqrestore(&iosapic_lock, flags);
223 }
224
225 static void
226 nop (unsigned int vector)
227 {
228         /* do nothing... */
229 }
230
231 static void
232 mask_irq (unsigned int irq)
233 {
234         unsigned long flags;
235         char *addr;
236         u32 low32;
237         int rte_index;
238         ia64_vector vec = irq_to_vector(irq);
239
240         addr = iosapic_intr_info[vec].addr;
241         rte_index = iosapic_intr_info[vec].rte_index;
242
243         if (rte_index < 0)
244                 return;                 /* not an IOSAPIC interrupt! */
245
246         spin_lock_irqsave(&iosapic_lock, flags);
247         {
248                 writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
249
250                 /* set only the mask bit */
251                 low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
252
253                 writel(low32, addr + IOSAPIC_WINDOW);
254         }
255         spin_unlock_irqrestore(&iosapic_lock, flags);
256 }
257
258 static void
259 unmask_irq (unsigned int irq)
260 {
261         unsigned long flags;
262         char *addr;
263         u32 low32;
264         int rte_index;
265         ia64_vector vec = irq_to_vector(irq);
266
267         addr = iosapic_intr_info[vec].addr;
268         rte_index = iosapic_intr_info[vec].rte_index;
269         if (rte_index < 0)
270                 return;                 /* not an IOSAPIC interrupt! */
271
272         spin_lock_irqsave(&iosapic_lock, flags);
273         {
274                 writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
275                 low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
276                 writel(low32, addr + IOSAPIC_WINDOW);
277         }
278         spin_unlock_irqrestore(&iosapic_lock, flags);
279 }
280
281
282 static void
283 iosapic_set_affinity (unsigned int irq, cpumask_t mask)
284 {
285 #ifdef CONFIG_SMP
286         unsigned long flags;
287         u32 high32, low32;
288         int dest, rte_index;
289         char *addr;
290         int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
291         ia64_vector vec;
292
293         irq &= (~IA64_IRQ_REDIRECTED);
294         vec = irq_to_vector(irq);
295
296         if (cpus_empty(mask))
297                 return;
298
299         dest = cpu_physical_id(first_cpu(mask));
300
301         rte_index = iosapic_intr_info[vec].rte_index;
302         addr = iosapic_intr_info[vec].addr;
303
304         if (rte_index < 0)
305                 return;                 /* not an IOSAPIC interrupt */
306
307         set_irq_affinity_info(irq, dest, redir);
308
309         /* dest contains both id and eid */
310         high32 = dest << IOSAPIC_DEST_SHIFT;
311
312         spin_lock_irqsave(&iosapic_lock, flags);
313         {
314                 /* get current delivery mode by reading the low32 */
315                 writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
316                 low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);
317                 if (redir)
318                         /* change delivery mode to lowest priority */
319                         low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
320                 else
321                         /* change delivery mode to fixed */
322                         low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);
323
324                 iosapic_intr_info[vec].low32 = low32;
325                 writel(IOSAPIC_RTE_HIGH(rte_index), addr + IOSAPIC_REG_SELECT);
326                 writel(high32, addr + IOSAPIC_WINDOW);
327                 writel(IOSAPIC_RTE_LOW(rte_index), addr + IOSAPIC_REG_SELECT);
328                 writel(low32, addr + IOSAPIC_WINDOW);
329         }
330         spin_unlock_irqrestore(&iosapic_lock, flags);
331 #endif
332 }
333
334 /*
335  * Handlers for level-triggered interrupts.
336  */
337
338 static unsigned int
339 iosapic_startup_level_irq (unsigned int irq)
340 {
341         unmask_irq(irq);
342         return 0;
343 }
344
345 static void
346 iosapic_end_level_irq (unsigned int irq)
347 {
348         ia64_vector vec = irq_to_vector(irq);
349
350         writel(vec, iosapic_intr_info[vec].addr + IOSAPIC_EOI);
351 }
352
353 #define iosapic_shutdown_level_irq      mask_irq
354 #define iosapic_enable_level_irq        unmask_irq
355 #define iosapic_disable_level_irq       mask_irq
356 #define iosapic_ack_level_irq           nop
357
358 struct hw_interrupt_type irq_type_iosapic_level = {
359         .typename =     "IO-SAPIC-level",
360         .startup =      iosapic_startup_level_irq,
361         .shutdown =     iosapic_shutdown_level_irq,
362         .enable =       iosapic_enable_level_irq,
363         .disable =      iosapic_disable_level_irq,
364         .ack =          iosapic_ack_level_irq,
365         .end =          iosapic_end_level_irq,
366         .set_affinity = iosapic_set_affinity
367 };
368
369 /*
370  * Handlers for edge-triggered interrupts.
371  */
372
373 static unsigned int
374 iosapic_startup_edge_irq (unsigned int irq)
375 {
376         unmask_irq(irq);
377         /*
378          * IOSAPIC simply drops interrupts pended while the
379          * corresponding pin was masked, so we can't know if an
380          * interrupt is pending already.  Let's hope not...
381          */
382         return 0;
383 }
384
385 static void
386 iosapic_ack_edge_irq (unsigned int irq)
387 {
388         irq_desc_t *idesc = irq_descp(irq);
389         /*
390          * Once we have recorded IRQ_PENDING already, we can mask the
391          * interrupt for real. This prevents IRQ storms from unhandled
392          * devices.
393          */
394         if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED))
395                 mask_irq(irq);
396 }
397
398 #define iosapic_enable_edge_irq         unmask_irq
399 #define iosapic_disable_edge_irq        nop
400 #define iosapic_end_edge_irq            nop
401
402 struct hw_interrupt_type irq_type_iosapic_edge = {
403         .typename =     "IO-SAPIC-edge",
404         .startup =      iosapic_startup_edge_irq,
405         .shutdown =     iosapic_disable_edge_irq,
406         .enable =       iosapic_enable_edge_irq,
407         .disable =      iosapic_disable_edge_irq,
408         .ack =          iosapic_ack_edge_irq,
409         .end =          iosapic_end_edge_irq,
410         .set_affinity = iosapic_set_affinity
411 };
412
413 unsigned int
414 iosapic_version (char *addr)
415 {
416         /*
417          * IOSAPIC Version Register return 32 bit structure like:
418          * {
419          *      unsigned int version   : 8;
420          *      unsigned int reserved1 : 8;
421          *      unsigned int max_redir : 8;
422          *      unsigned int reserved2 : 8;
423          * }
424          */
425         writel(IOSAPIC_VERSION, addr + IOSAPIC_REG_SELECT);
426         return readl(IOSAPIC_WINDOW + addr);
427 }
428
429 /*
430  * if the given vector is already owned by other,
431  *  assign a new vector for the other and make the vector available
432  */
433 static void __init
434 iosapic_reassign_vector (int vector)
435 {
436         int new_vector;
437
438         if (iosapic_intr_info[vector].rte_index >= 0 || iosapic_intr_info[vector].addr
439             || iosapic_intr_info[vector].gsi_base || iosapic_intr_info[vector].dmode
440             || iosapic_intr_info[vector].polarity || iosapic_intr_info[vector].trigger)
441         {
442                 new_vector = assign_irq_vector(AUTO_ASSIGN);
443                 printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
444                 memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
445                        sizeof(struct iosapic_intr_info));
446                 memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
447                 iosapic_intr_info[vector].rte_index = -1;
448         }
449 }
450
451 static void
452 register_intr (unsigned int gsi, int vector, unsigned char delivery,
453                unsigned long polarity, unsigned long trigger)
454 {
455         irq_desc_t *idesc;
456         struct hw_interrupt_type *irq_type;
457         int rte_index;
458         int index;
459         unsigned long gsi_base;
460         char *iosapic_address;
461
462         index = find_iosapic(gsi);
463         if (index < 0) {
464                 printk(KERN_WARNING "%s: No IOSAPIC for GSI 0x%x\n", __FUNCTION__, gsi);
465                 return;
466         }
467
468         iosapic_address = iosapic_lists[index].addr;
469         gsi_base = iosapic_lists[index].gsi_base;
470
471         rte_index = gsi - gsi_base;
472         iosapic_intr_info[vector].rte_index = rte_index;
473         iosapic_intr_info[vector].polarity = polarity;
474         iosapic_intr_info[vector].dmode    = delivery;
475         iosapic_intr_info[vector].addr     = iosapic_address;
476         iosapic_intr_info[vector].gsi_base = gsi_base;
477         iosapic_intr_info[vector].trigger  = trigger;
478
479         if (trigger == IOSAPIC_EDGE)
480                 irq_type = &irq_type_iosapic_edge;
481         else
482                 irq_type = &irq_type_iosapic_level;
483
484         idesc = irq_descp(vector);
485         if (idesc->handler != irq_type) {
486                 if (idesc->handler != &no_irq_type)
487                         printk(KERN_WARNING "%s: changing vector %d from %s to %s\n",
488                                __FUNCTION__, vector, idesc->handler->typename, irq_type->typename);
489                 idesc->handler = irq_type;
490         }
491 }
492
493 /*
494  * ACPI can describe IOSAPIC interrupts via static tables and namespace
495  * methods.  This provides an interface to register those interrupts and
496  * program the IOSAPIC RTE.
497  */
498 int
499 iosapic_register_intr (unsigned int gsi,
500                        unsigned long polarity, unsigned long trigger)
501 {
502         int vector;
503         unsigned int dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
504
505         vector = gsi_to_vector(gsi);
506         if (vector < 0)
507                 vector = assign_irq_vector(AUTO_ASSIGN);
508
509         register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
510                       polarity, trigger);
511
512         printk(KERN_INFO "GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n",
513                gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
514                (trigger == IOSAPIC_EDGE ? "edge" : "level"), dest, vector);
515
516         /* program the IOSAPIC routing table */
517         set_rte(vector, dest, 0);
518         return vector;
519 }
520
521 /*
522  * ACPI calls this when it finds an entry for a platform interrupt.
523  * Note that the irq_base and IOSAPIC address must be set in iosapic_init().
524  */
525 int __init
526 iosapic_register_platform_intr (u32 int_type, unsigned int gsi,
527                                 int iosapic_vector, u16 eid, u16 id,
528                                 unsigned long polarity, unsigned long trigger)
529 {
530         unsigned char delivery;
531         int vector;
532         unsigned int dest = ((id << 8) | eid) & 0xffff;
533
534         switch (int_type) {
535               case ACPI_INTERRUPT_PMI:
536                 vector = iosapic_vector;
537                 /*
538                  * since PMI vector is alloc'd by FW(ACPI) not by kernel,
539                  * we need to make sure the vector is available
540                  */
541                 iosapic_reassign_vector(vector);
542                 delivery = IOSAPIC_PMI;
543                 break;
544               case ACPI_INTERRUPT_INIT:
545                 vector = assign_irq_vector(AUTO_ASSIGN);
546                 delivery = IOSAPIC_INIT;
547                 break;
548               case ACPI_INTERRUPT_CPEI:
549                 vector = IA64_CPE_VECTOR;
550                 delivery = IOSAPIC_LOWEST_PRIORITY;
551                 break;
552               default:
553                 printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type\n");
554                 return -1;
555         }
556
557         register_intr(gsi, vector, delivery, polarity,
558                       trigger);
559
560         printk(KERN_INFO "PLATFORM int 0x%x: GSI 0x%x(%s,%s) -> CPU 0x%04x vector %d\n",
561                int_type, gsi, (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
562                (trigger == IOSAPIC_EDGE ? "edge" : "level"), dest, vector);
563
564         /* program the IOSAPIC routing table */
565         set_rte(vector, dest, 0);
566         return vector;
567 }
568
569
570 /*
571  * ACPI calls this when it finds an entry for a legacy ISA IRQ override.
572  * Note that the gsi_base and IOSAPIC address must be set in iosapic_init().
573  */
574 void __init
575 iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
576                           unsigned long polarity,
577                           unsigned long trigger)
578 {
579         int vector;
580         unsigned int dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
581
582         vector = isa_irq_to_vector(isa_irq);
583
584         register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);
585
586         DBG("ISA: IRQ %u -> GSI 0x%x (%s,%s) -> CPU 0x%04x vector %d\n",
587             isa_irq, gsi, polarity == IOSAPIC_POL_HIGH ? "high" : "low",
588             trigger == IOSAPIC_EDGE ? "edge" : "level", dest, vector);
589
590         /* program the IOSAPIC routing table */
591         set_rte(vector, dest, 0);
592 }
593
594 void __init
595 iosapic_system_init (int system_pcat_compat)
596 {
597         int vector;
598
599         for (vector = 0; vector < IA64_NUM_VECTORS; ++vector)
600                 iosapic_intr_info[vector].rte_index = -1;       /* mark as unused */
601
602         pcat_compat = system_pcat_compat;
603         if (pcat_compat) {
604                 /*
605                  * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
606                  * enabled.
607                  */
608                 printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
609                 outb(0xff, 0xA1);
610                 outb(0xff, 0x21);
611         }
612 }
613
614 void __init
615 iosapic_init (unsigned long phys_addr, unsigned int gsi_base)
616 {
617         int num_rte;
618         unsigned int isa_irq, ver;
619         char *addr;
620
621         addr = ioremap(phys_addr, 0);
622         ver = iosapic_version(addr);
623
624         /*
625          * The MAX_REDIR register holds the highest input pin
626          * number (starting from 0).
627          * We add 1 so that we can use it for number of pins (= RTEs)
628          */
629         num_rte = ((ver >> 16) & 0xff) + 1;
630
631         iosapic_lists[num_iosapic].addr = addr;
632         iosapic_lists[num_iosapic].gsi_base = gsi_base;
633         iosapic_lists[num_iosapic].num_rte = num_rte;
634         num_iosapic++;
635
636         if ((gsi_base == 0) && pcat_compat) {
637                 /*
638                  * Map the legacy ISA devices into the IOSAPIC data.  Some of these may
639                  * get reprogrammed later on with data from the ACPI Interrupt Source
640                  * Override table.
641                  */
642                 for (isa_irq = 0; isa_irq < 16; ++isa_irq)
643                         iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE);
644         }
645 }
646
647 void
648 iosapic_enable_intr (unsigned int vector)
649 {
650         unsigned int dest;
651         irq_desc_t *desc;
652
653         /*
654          * In the case of a shared interrupt, do not re-route the vector, and
655          * especially do not mask a running interrupt (startup will not get
656          * called for a shared interrupt).
657          */
658         desc = irq_descp(vector);
659         if (desc->action)
660                 return;
661
662 #ifdef CONFIG_SMP
663         /*
664          * For platforms that do not support interrupt redirect via the XTP interface, we
665          * can round-robin the PCI device interrupts to the processors
666          */
667         if (!(smp_int_redirect & SMP_IRQ_REDIRECTION)) {
668                 static int cpu_index = -1;
669
670                 do
671                         if (++cpu_index >= NR_CPUS)
672                                 cpu_index = 0;
673                 while (!cpu_online(cpu_index));
674
675                 dest = cpu_physical_id(cpu_index) & 0xffff;
676         } else {
677                 /*
678                  * Direct the interrupt vector to the current cpu, platform redirection
679                  * will distribute them.
680                  */
681                 dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
682         }
683 #else
684         /* direct the interrupt vector to the running cpu id */
685         dest = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
686 #endif
687         set_rte(vector, dest, 1);
688
689         printk(KERN_INFO "IOSAPIC: vector %d -> CPU 0x%04x, enabled\n",
690                vector, dest);
691 }
692
693 #ifdef CONFIG_ACPI_PCI
694
695 void __init
696 iosapic_parse_prt (void)
697 {
698         struct acpi_prt_entry *entry;
699         struct list_head *node;
700         unsigned int gsi;
701         int vector;
702         char pci_id[16];
703         struct hw_interrupt_type *irq_type = &irq_type_iosapic_level;
704         irq_desc_t *idesc;
705
706         list_for_each(node, &acpi_prt.entries) {
707                 entry = list_entry(node, struct acpi_prt_entry, node);
708
709                 /* We're only interested in static (non-link) entries.  */
710                 if (entry->link.handle)
711                         continue;
712
713                 gsi = entry->link.index;
714
715                 vector = gsi_to_vector(gsi);
716                 if (vector < 0) {
717                         if (find_iosapic(gsi) < 0)
718                                 continue;
719
720                         /* allocate a vector for this interrupt line */
721                         if (pcat_compat && (gsi < 16))
722                                 vector = isa_irq_to_vector(gsi);
723                         else
724                                 /* new GSI; allocate a vector for it */
725                                 vector = assign_irq_vector(AUTO_ASSIGN);
726
727                         register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW,
728                                       IOSAPIC_LEVEL);
729                 }
730                 entry->irq = vector;
731                 snprintf(pci_id, sizeof(pci_id), "%02x:%02x:%02x[%c]",
732                          entry->id.segment, entry->id.bus, entry->id.device, 'A' + entry->pin);
733
734                 /*
735                  * If vector was previously initialized to a different
736                  * handler, re-initialize.
737                  */
738                 idesc = irq_descp(vector);
739                 if (idesc->handler != irq_type)
740                         register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW,
741                                       IOSAPIC_LEVEL);
742
743         }
744 }
745
746 #endif /* CONFIG_ACPI */