]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/x86/kernel/apic_visws.c
x86, VisWS: turn into generic arch, copy visws files
[linux-2.6.git] / arch / x86 / kernel / apic_visws.c
1 /*
2  *      Copyright (C) 1999 Bent Hagemark, Ingo Molnar
3  *
4  *  SGI Visual Workstation interrupt controller
5  *
6  *  The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC
7  *  which serves as the main interrupt controller in the system.  Non-legacy
8  *  hardware in the system uses this controller directly.  Legacy devices
9  *  are connected to the PIIX4 which in turn has its 8259(s) connected to
10  *  a of the Cobalt APIC entry.
11  *
12  *  09/02/2000 - Updated for 2.4 by jbarnes@sgi.com
13  *
14  *  25/11/2002 - Updated for 2.5 by Andrey Panin <pazke@orbita1.ru>
15  */
16
17 #include <linux/kernel_stat.h>
18 #include <linux/interrupt.h>
19 #include <linux/init.h>
20
21 #include <asm/io.h>
22 #include <asm/apic.h>
23 #include <asm/i8259.h>
24 #include <asm/irq_vectors.h>
25 #include <asm/visws/cobalt.h>
26
27 static DEFINE_SPINLOCK(cobalt_lock);
28
29 /*
30  * Set the given Cobalt APIC Redirection Table entry to point
31  * to the given IDT vector/index.
32  */
33 static inline void co_apic_set(int entry, int irq)
34 {
35         co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (irq + FIRST_EXTERNAL_VECTOR));
36         co_apic_write(CO_APIC_HI(entry), 0);
37 }
38
39 /*
40  * Cobalt (IO)-APIC functions to handle PCI devices.
41  */
42 static inline int co_apic_ide0_hack(void)
43 {
44         extern char visws_board_type;
45         extern char visws_board_rev;
46
47         if (visws_board_type == VISWS_320 && visws_board_rev == 5)
48                 return 5;
49         return CO_APIC_IDE0;
50 }
51
52 static int is_co_apic(unsigned int irq)
53 {
54         if (IS_CO_APIC(irq))
55                 return CO_APIC(irq);
56
57         switch (irq) {
58                 case 0: return CO_APIC_CPU;
59                 case CO_IRQ_IDE0: return co_apic_ide0_hack();
60                 case CO_IRQ_IDE1: return CO_APIC_IDE1;
61                 default: return -1;
62         }
63 }
64
65
66 /*
67  * This is the SGI Cobalt (IO-)APIC:
68  */
69
70 static void enable_cobalt_irq(unsigned int irq)
71 {
72         co_apic_set(is_co_apic(irq), irq);
73 }
74
75 static void disable_cobalt_irq(unsigned int irq)
76 {
77         int entry = is_co_apic(irq);
78
79         co_apic_write(CO_APIC_LO(entry), CO_APIC_MASK);
80         co_apic_read(CO_APIC_LO(entry));
81 }
82
83 /*
84  * "irq" really just serves to identify the device.  Here is where we
85  * map this to the Cobalt APIC entry where it's physically wired.
86  * This is called via request_irq -> setup_irq -> irq_desc->startup()
87  */
88 static unsigned int startup_cobalt_irq(unsigned int irq)
89 {
90         unsigned long flags;
91
92         spin_lock_irqsave(&cobalt_lock, flags);
93         if ((irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS | IRQ_WAITING)))
94                 irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS | IRQ_WAITING);
95         enable_cobalt_irq(irq);
96         spin_unlock_irqrestore(&cobalt_lock, flags);
97         return 0;
98 }
99
100 static void ack_cobalt_irq(unsigned int irq)
101 {
102         unsigned long flags;
103
104         spin_lock_irqsave(&cobalt_lock, flags);
105         disable_cobalt_irq(irq);
106         apic_write(APIC_EOI, APIC_EIO_ACK);
107         spin_unlock_irqrestore(&cobalt_lock, flags);
108 }
109
110 static void end_cobalt_irq(unsigned int irq)
111 {
112         unsigned long flags;
113
114         spin_lock_irqsave(&cobalt_lock, flags);
115         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
116                 enable_cobalt_irq(irq);
117         spin_unlock_irqrestore(&cobalt_lock, flags);
118 }
119
120 static struct irq_chip cobalt_irq_type = {
121         .typename =     "Cobalt-APIC",
122         .startup =      startup_cobalt_irq,
123         .shutdown =     disable_cobalt_irq,
124         .enable =       enable_cobalt_irq,
125         .disable =      disable_cobalt_irq,
126         .ack =          ack_cobalt_irq,
127         .end =          end_cobalt_irq,
128 };
129
130
131 /*
132  * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt
133  * -- not the manner expected by the code in i8259.c.
134  *
135  * there is a 'master' physical interrupt source that gets sent to
136  * the CPU. But in the chipset there are various 'virtual' interrupts
137  * waiting to be handled. We represent this to Linux through a 'master'
138  * interrupt controller type, and through a special virtual interrupt-
139  * controller. Device drivers only see the virtual interrupt sources.
140  */
141 static unsigned int startup_piix4_master_irq(unsigned int irq)
142 {
143         init_8259A(0);
144
145         return startup_cobalt_irq(irq);
146 }
147
148 static void end_piix4_master_irq(unsigned int irq)
149 {
150         unsigned long flags;
151
152         spin_lock_irqsave(&cobalt_lock, flags);
153         enable_cobalt_irq(irq);
154         spin_unlock_irqrestore(&cobalt_lock, flags);
155 }
156
157 static struct irq_chip piix4_master_irq_type = {
158         .typename =     "PIIX4-master",
159         .startup =      startup_piix4_master_irq,
160         .ack =          ack_cobalt_irq,
161         .end =          end_piix4_master_irq,
162 };
163
164
165 static struct irq_chip piix4_virtual_irq_type = {
166         .typename =     "PIIX4-virtual",
167         .shutdown =     disable_8259A_irq,
168         .enable =       enable_8259A_irq,
169         .disable =      disable_8259A_irq,
170 };
171
172
173 /*
174  * PIIX4-8259 master/virtual functions to handle interrupt requests
175  * from legacy devices: floppy, parallel, serial, rtc.
176  *
177  * None of these get Cobalt APIC entries, neither do they have IDT
178  * entries. These interrupts are purely virtual and distributed from
179  * the 'master' interrupt source: CO_IRQ_8259.
180  *
181  * When the 8259 interrupts its handler figures out which of these
182  * devices is interrupting and dispatches to its handler.
183  *
184  * CAREFUL: devices see the 'virtual' interrupt only. Thus disable/
185  * enable_irq gets the right irq. This 'master' irq is never directly
186  * manipulated by any driver.
187  */
188 static irqreturn_t piix4_master_intr(int irq, void *dev_id)
189 {
190         int realirq;
191         irq_desc_t *desc;
192         unsigned long flags;
193
194         spin_lock_irqsave(&i8259A_lock, flags);
195
196         /* Find out what's interrupting in the PIIX4 master 8259 */
197         outb(0x0c, 0x20);               /* OCW3 Poll command */
198         realirq = inb(0x20);
199
200         /*
201          * Bit 7 == 0 means invalid/spurious
202          */
203         if (unlikely(!(realirq & 0x80)))
204                 goto out_unlock;
205
206         realirq &= 7;
207
208         if (unlikely(realirq == 2)) {
209                 outb(0x0c, 0xa0);
210                 realirq = inb(0xa0);
211
212                 if (unlikely(!(realirq & 0x80)))
213                         goto out_unlock;
214
215                 realirq = (realirq & 7) + 8;
216         }
217
218         /* mask and ack interrupt */
219         cached_irq_mask |= 1 << realirq;
220         if (unlikely(realirq > 7)) {
221                 inb(0xa1);
222                 outb(cached_slave_mask, 0xa1);
223                 outb(0x60 + (realirq & 7), 0xa0);
224                 outb(0x60 + 2, 0x20);
225         } else {
226                 inb(0x21);
227                 outb(cached_master_mask, 0x21);
228                 outb(0x60 + realirq, 0x20);
229         }
230
231         spin_unlock_irqrestore(&i8259A_lock, flags);
232
233         desc = irq_desc + realirq;
234
235         /*
236          * handle this 'virtual interrupt' as a Cobalt one now.
237          */
238         kstat_cpu(smp_processor_id()).irqs[realirq]++;
239
240         if (likely(desc->action != NULL))
241                 handle_IRQ_event(realirq, desc->action);
242
243         if (!(desc->status & IRQ_DISABLED))
244                 enable_8259A_irq(realirq);
245
246         return IRQ_HANDLED;
247
248 out_unlock:
249         spin_unlock_irqrestore(&i8259A_lock, flags);
250         return IRQ_NONE;
251 }
252
253 static struct irqaction master_action = {
254         .handler =      piix4_master_intr,
255         .name =         "PIIX4-8259",
256 };
257
258 static struct irqaction cascade_action = {
259         .handler =      no_action,
260         .name =         "cascade",
261 };
262
263
264 void init_VISWS_APIC_irqs(void)
265 {
266         int i;
267
268         for (i = 0; i < CO_IRQ_APIC0 + CO_APIC_LAST + 1; i++) {
269                 irq_desc[i].status = IRQ_DISABLED;
270                 irq_desc[i].action = 0;
271                 irq_desc[i].depth = 1;
272
273                 if (i == 0) {
274                         irq_desc[i].chip = &cobalt_irq_type;
275                 }
276                 else if (i == CO_IRQ_IDE0) {
277                         irq_desc[i].chip = &cobalt_irq_type;
278                 }
279                 else if (i == CO_IRQ_IDE1) {
280                         irq_desc[i].chip = &cobalt_irq_type;
281                 }
282                 else if (i == CO_IRQ_8259) {
283                         irq_desc[i].chip = &piix4_master_irq_type;
284                 }
285                 else if (i < CO_IRQ_APIC0) {
286                         irq_desc[i].chip = &piix4_virtual_irq_type;
287                 }
288                 else if (IS_CO_APIC(i)) {
289                         irq_desc[i].chip = &cobalt_irq_type;
290                 }
291         }
292
293         setup_irq(CO_IRQ_8259, &master_action);
294         setup_irq(2, &cascade_action);
295 }