Import changeset
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / kernel / irq_ia64.c
1 /*
2  * linux/arch/ia64/kernel/irq.c
3  *
4  * Copyright (C) 1998-2000 Hewlett-Packard Co
5  * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com>
6  * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
7  *
8  *  6/10/99: Updated to bring in sync with x86 version to facilitate
9  *           support for SMP and different interrupt controllers.
10  *
11  * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
12  *                      PCI to vector allocation routine.
13  */
14
15 #include <linux/config.h>
16
17 #include <linux/sched.h>
18 #include <linux/errno.h>
19 #include <linux/init.h>
20 #include <linux/interrupt.h>
21 #include <linux/ioport.h>
22 #include <linux/kernel_stat.h>
23 #include <linux/malloc.h>
24 #include <linux/ptrace.h>
25 #include <linux/random.h>       /* for rand_initialize_irq() */
26 #include <linux/signal.h>
27 #include <linux/smp.h>
28 #include <linux/smp_lock.h>
29 #include <linux/threads.h>
30
31 #include <asm/bitops.h>
32 #include <asm/delay.h>
33 #include <asm/io.h>
34 #include <asm/hw_irq.h>
35 #include <asm/machvec.h>
36 #include <asm/pgtable.h>
37 #include <asm/system.h>
38
39 #define IRQ_DEBUG       0
40
41 /* default base addr of IPI table */
42 unsigned long ipi_base_addr = (__IA64_UNCACHED_OFFSET | IPI_DEFAULT_BASE_ADDR); 
43
44 /*
45  * Legacy IRQ to IA-64 vector translation table.
46  */
47 __u8 isa_irq_to_vector_map[16] = {
48         /* 8259 IRQ translation, first 16 entries */
49         0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
50         0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
51 };
52
53 int
54 ia64_alloc_irq (void)
55 {
56         static int next_irq = FIRST_DEVICE_IRQ;
57
58         if (next_irq > LAST_DEVICE_IRQ)
59                 /* XXX could look for sharable vectors instead of panic'ing... */
60                 panic("ia64_alloc_irq: out of interrupt vectors!");
61         return next_irq++;
62 }
63
64 /*
65  * That's where the IVT branches when we get an external
66  * interrupt. This branches to the correct hardware IRQ handler via
67  * function ptr.
68  */
69 void
70 ia64_handle_irq (unsigned long vector, struct pt_regs *regs)
71 {
72         unsigned long saved_tpr;
73
74 #if IRQ_DEBUG
75         {
76                 unsigned long bsp, sp;
77
78                 /*
79                  * Note: if the interrupt happened while executing in
80                  * the context switch routine (ia64_switch_to), we may
81                  * get a spurious stack overflow here.  This is
82                  * because the register and the memory stack are not
83                  * switched atomically.
84                  */
85                 asm ("mov %0=ar.bsp" : "=r"(bsp));
86                 asm ("mov %0=sp" : "=r"(sp));
87
88                 if ((sp - bsp) < 1024) {
89                         static unsigned char count;
90                         static long last_time;
91
92                         if (count > 5 && jiffies - last_time > 5*HZ)
93                                 count = 0;
94                         if (++count < 5) {
95                                 last_time = jiffies;
96                                 printk("ia64_handle_irq: DANGER: less than "
97                                        "1KB of free stack space!!\n"
98                                        "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
99                         }
100                 }
101         }
102 #endif /* IRQ_DEBUG */
103
104         /*
105          * Always set TPR to limit maximum interrupt nesting depth to
106          * 16 (without this, it would be ~240, which could easily lead
107          * to kernel stack overflows).
108          */
109         saved_tpr = ia64_get_tpr();
110         ia64_srlz_d();
111         do {
112                 if (vector >= NR_IRQS) {
113                         printk("handle_irq: invalid vector %lu\n", vector);
114                         ia64_set_tpr(saved_tpr);
115                         ia64_srlz_d();
116                         return;
117                 }
118                 ia64_set_tpr(vector);
119                 ia64_srlz_d();
120
121                 if ((irq_desc[vector].status & IRQ_PER_CPU) != 0)
122                         do_IRQ_per_cpu(vector, regs);
123                 else
124                         do_IRQ(vector, regs);
125
126                 /*
127                  * Disable interrupts and send EOI:
128                  */
129                 local_irq_disable();
130                 ia64_set_tpr(saved_tpr);
131                 ia64_eoi();
132                 vector = ia64_get_ivr();
133         } while (vector != IA64_SPURIOUS_INT);
134 }
135
136 #ifdef CONFIG_SMP
137
138 extern void handle_IPI (int irq, void *dev_id, struct pt_regs *regs);
139
140 static struct irqaction ipi_irqaction = {
141         handler:        handle_IPI,
142         flags:          SA_INTERRUPT,
143         name:           "IPI"
144 };
145 #endif
146
147 void __init
148 init_IRQ (void)
149 {
150         /*
151          * Disable all local interrupts
152          */
153         ia64_set_itv(0, 1);
154         ia64_set_lrr0(0, 1);
155         ia64_set_lrr1(0, 1);
156
157         irq_desc[IA64_SPURIOUS_INT].handler = &irq_type_ia64_sapic;
158 #ifdef CONFIG_SMP
159         /* 
160          * Configure the IPI vector and handler
161          */
162         irq_desc[IPI_IRQ].status |= IRQ_PER_CPU;
163         irq_desc[IPI_IRQ].handler = &irq_type_ia64_sapic;
164         setup_irq(IPI_IRQ, &ipi_irqaction);
165 #endif
166
167         ia64_set_pmv(1 << 16);
168         ia64_set_cmcv(CMC_IRQ);                 /* XXX fix me */
169
170         platform_irq_init();
171
172         /* clear TPR to enable all interrupt classes: */
173         ia64_set_tpr(0);
174 }
175
176 void
177 ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
178 {
179         unsigned long ipi_addr;
180         unsigned long ipi_data;
181         unsigned long phys_cpu_id;
182
183 #ifdef CONFIG_SMP
184         phys_cpu_id = cpu_physical_id(cpu);
185 #else
186         phys_cpu_id = (ia64_get_lid() >> 16) & 0xffff;
187 #endif
188
189         /*
190          * cpu number is in 8bit ID and 8bit EID
191          */
192
193         ipi_data = (delivery_mode << 8) | (vector & 0xff);
194         ipi_addr = ipi_base_addr | (phys_cpu_id << 4) | ((redirect & 1)  << 3);
195
196         writeq(ipi_data, ipi_addr);
197 }