more changes on original files
[linux-2.4.git] / arch / i386 / kernel / visws_apic.c
1 /*
2  *      linux/arch/i386/kernel/visws_apic.c
3  *
4  *      Copyright (C) 1999 Bent Hagemark, Ingo Molnar
5  *
6  *  SGI Visual Workstation interrupt controller
7  *
8  *  The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC
9  *  which serves as the main interrupt controller in the system.  Non-legacy
10  *  hardware in the system uses this controller directly.  Legacy devices
11  *  are connected to the PIIX4 which in turn has its 8259(s) connected to
12  *  a of the Cobalt APIC entry.
13  */
14
15 #include <linux/ptrace.h>
16 #include <linux/errno.h>
17 #include <linux/kernel_stat.h>
18 #include <linux/signal.h>
19 #include <linux/sched.h>
20 #include <linux/ioport.h>
21 #include <linux/interrupt.h>
22 #include <linux/timex.h>
23 #include <linux/slab.h>
24 #include <linux/random.h>
25 #include <linux/smp.h>
26 #include <linux/smp_lock.h>
27 #include <linux/init.h>
28
29 #include <asm/system.h>
30 #include <asm/io.h>
31 #include <asm/irq.h>
32 #include <asm/bitops.h>
33 #include <asm/smp.h>
34 #include <asm/pgtable.h>
35 #include <asm/delay.h>
36 #include <asm/desc.h>
37
38 #include <asm/cobalt.h>
39
40 #include <linux/irq.h>
41
42 /*
43  * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt
44  * -- not the manner expected by the normal 8259 code in irq.c.
45  *
46  * there is a 'master' physical interrupt source that gets sent to
47  * the CPU. But in the chipset there are various 'virtual' interrupts
48  * waiting to be handled. We represent this to Linux through a 'master'
49  * interrupt controller type, and through a special virtual interrupt-
50  * controller. Device drivers only see the virtual interrupt sources.
51  */
52
53 #define CO_IRQ_BASE     0x20    /* This is the 0x20 in init_IRQ()! */
54
55 static void startup_piix4_master_irq(unsigned int irq);
56 static void shutdown_piix4_master_irq(unsigned int irq);
57 static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs);
58 #define enable_piix4_master_irq startup_piix4_master_irq
59 #define disable_piix4_master_irq shutdown_piix4_master_irq
60
61 static struct hw_interrupt_type piix4_master_irq_type = {
62         "PIIX4-master",
63         startup_piix4_master_irq,
64         shutdown_piix4_master_irq,
65         do_piix4_master_IRQ,
66         enable_piix4_master_irq,
67         disable_piix4_master_irq
68 };
69
70 static void enable_piix4_virtual_irq(unsigned int irq);
71 static void disable_piix4_virtual_irq(unsigned int irq);
72 #define startup_piix4_virtual_irq enable_piix4_virtual_irq
73 #define shutdown_piix4_virtual_irq disable_piix4_virtual_irq
74
75 static struct hw_interrupt_type piix4_virtual_irq_type = {
76         "PIIX4-virtual",
77         startup_piix4_virtual_irq,
78         shutdown_piix4_virtual_irq,
79         0, /* no handler, it's never called physically */
80         enable_piix4_virtual_irq,
81         disable_piix4_virtual_irq
82 };
83
84 /*
85  * This is the SGI Cobalt (IO-)APIC:
86  */
87
88 static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs);
89 static void enable_cobalt_irq(unsigned int irq);
90 static void disable_cobalt_irq(unsigned int irq);
91 static void startup_cobalt_irq(unsigned int irq);
92 #define shutdown_cobalt_irq disable_cobalt_irq
93
94 static spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;
95
96 static struct hw_interrupt_type cobalt_irq_type = {
97         "Cobalt-APIC",
98         startup_cobalt_irq,
99         shutdown_cobalt_irq,
100         do_cobalt_IRQ,
101         enable_cobalt_irq,
102         disable_cobalt_irq
103 };
104
105
106 /*
107  * Not an __init, needed by the reboot code
108  */
109 void disable_IO_APIC(void)
110 {
111         /* Nop on Cobalt */
112
113
114 /*
115  * Cobalt (IO)-APIC functions to handle PCI devices.
116  */
117
118 static void disable_cobalt_irq(unsigned int irq)
119 {
120         /* XXX undo the APIC entry here? */
121
122         /*
123          * definitely, we do not want to have IRQ storms from
124          * unused devices --mingo
125          */
126 }
127
128 static void enable_cobalt_irq(unsigned int irq)
129 {
130 }
131
132 /*
133  * Set the given Cobalt APIC Redirection Table entry to point
134  * to the given IDT vector/index.
135  */
136 static void co_apic_set(int entry, int idtvec)
137 {
138         co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (CO_IRQ_BASE+idtvec));
139         co_apic_write(CO_APIC_HI(entry), 0);
140
141         printk("Cobalt APIC Entry %d IDT Vector %d\n", entry, idtvec);
142 }
143
144 /*
145  * "irq" really just serves to identify the device.  Here is where we
146  * map this to the Cobalt APIC entry where it's physically wired.
147  * This is called via request_irq -> setup_x86_irq -> irq_desc->startup()
148  */
149 static void startup_cobalt_irq(unsigned int irq)
150 {
151         /*
152          * These "irq"'s are wired to the same Cobalt APIC entries
153          * for all (known) motherboard types/revs
154          */
155         switch (irq) {
156         case CO_IRQ_TIMER:      co_apic_set(CO_APIC_CPU, CO_IRQ_TIMER);
157                                 return;
158
159         case CO_IRQ_ENET:       co_apic_set(CO_APIC_ENET, CO_IRQ_ENET);
160                                 return;
161
162         case CO_IRQ_SERIAL:     return; /* XXX move to piix4-8259 "virtual" */
163
164         case CO_IRQ_8259:       co_apic_set(CO_APIC_8259, CO_IRQ_8259);
165                                 return;
166
167         case CO_IRQ_IDE:
168                 switch (visws_board_type) {
169                 case VISWS_320:
170                         switch (visws_board_rev) {
171                         case 5:
172                                 co_apic_set(CO_APIC_0_5_IDE0, CO_IRQ_IDE);
173                                 co_apic_set(CO_APIC_0_5_IDE1, CO_IRQ_IDE);
174                                         return;
175                         case 6:
176                                 co_apic_set(CO_APIC_0_6_IDE0, CO_IRQ_IDE);
177                                 co_apic_set(CO_APIC_0_6_IDE1, CO_IRQ_IDE);
178                                         return;
179                         }
180                 case VISWS_540:
181                         switch (visws_board_rev) {
182                         case 2:
183                                 co_apic_set(CO_APIC_1_2_IDE0, CO_IRQ_IDE);
184                                         return;
185                         }
186                 }
187                 break;
188         default:
189                 panic("huh?");
190         }
191 }
192
193 /*
194  * This is the handle() op in do_IRQ()
195  */
196 static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs)
197 {
198         struct irqaction * action;
199         irq_desc_t *desc = irq_desc + irq;
200
201         spin_lock(&irq_controller_lock);
202         {
203                 unsigned int status;
204                 /* XXX APIC EOI? */
205                 status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
206                 action = NULL;
207                 if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
208                         action = desc->action;
209                         status |= IRQ_INPROGRESS;
210                 }
211                 desc->status = status;
212         }
213         spin_unlock(&irq_controller_lock);
214
215         /* Exit early if we had no action or it was disabled */
216         if (!action)
217                 return;
218
219         handle_IRQ_event(irq, regs, action);
220
221         (void)co_cpu_read(CO_CPU_REV); /* Sync driver ack to its h/w */
222         apic_write(APIC_EOI, APIC_EIO_ACK); /* Send EOI to Cobalt APIC */
223
224         spin_lock(&irq_controller_lock);
225         {
226                 unsigned int status = desc->status & ~IRQ_INPROGRESS;
227                 desc->status = status;
228                 if (!(status & IRQ_DISABLED))
229                         enable_cobalt_irq(irq);
230         }
231         spin_unlock(&irq_controller_lock);
232 }
233
234 /*
235  * PIIX4-8259 master/virtual functions to handle:
236  *
237  *      floppy
238  *      parallel
239  *      serial
240  *      audio (?)
241  *
242  * None of these get Cobalt APIC entries, neither do they have IDT
243  * entries. These interrupts are purely virtual and distributed from
244  * the 'master' interrupt source: CO_IRQ_8259.
245  *
246  * When the 8259 interrupts its handler figures out which of these
247  * devices is interrupting and dispatches to it's handler.
248  *
249  * CAREFUL: devices see the 'virtual' interrupt only. Thus disable/
250  * enable_irq gets the right irq. This 'master' irq is never directly
251  * manipulated by any driver.
252  */
253
254 static void startup_piix4_master_irq(unsigned int irq)
255 {
256         /* ICW1 */
257         outb(0x11, 0x20);
258         outb(0x11, 0xa0);
259
260         /* ICW2 */
261         outb(0x08, 0x21);
262         outb(0x70, 0xa1);
263
264         /* ICW3 */
265         outb(0x04, 0x21);
266         outb(0x02, 0xa1);
267
268         /* ICW4 */
269         outb(0x01, 0x21);
270         outb(0x01, 0xa1);
271
272         /* OCW1 - disable all interrupts in both 8259's */
273         outb(0xff, 0x21);
274         outb(0xff, 0xa1);
275
276         startup_cobalt_irq(irq);
277 }
278
279 static void shutdown_piix4_master_irq(unsigned int irq)
280 {
281         /*
282          * [we skip the 8259 magic here, not strictly necessary]
283          */
284
285         shutdown_cobalt_irq(irq);
286 }
287
288 static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs)
289 {
290         int realirq, mask;
291
292         /* Find out what's interrupting in the PIIX4 8259 */
293
294         spin_lock(&irq_controller_lock);
295         outb(0x0c, 0x20);               /* OCW3 Poll command */
296         realirq = inb(0x20);
297
298         if (!(realirq & 0x80)) {
299                 /*
300                  * Bit 7 == 0 means invalid/spurious
301                  */
302                 goto out_unlock;
303         }
304         realirq &= 0x7f;
305
306         /*
307          * mask and ack the 8259
308          */
309         mask = inb(0x21);
310         if ((mask >> realirq) & 0x01)
311                 /*
312                  * This IRQ is masked... ignore
313                  */
314                 goto out_unlock;
315
316         outb(mask | (1<<realirq), 0x21);
317         /*
318          * OCW2 - non-specific EOI
319          */
320         outb(0x20, 0x20);
321
322         spin_unlock(&irq_controller_lock);
323
324         /*
325          * handle this 'virtual interrupt' as a Cobalt one now.
326          */
327         kstat.irqs[smp_processor_id()][irq]++;
328         do_cobalt_IRQ(realirq, regs);
329
330         spin_lock(&irq_controller_lock);
331         {
332                 irq_desc_t *desc = irq_desc + realirq;
333
334                 if (!(desc->status & IRQ_DISABLED))
335                         enable_piix4_virtual_irq(realirq);
336         }
337         spin_unlock(&irq_controller_lock);
338         return;
339
340 out_unlock:
341         spin_unlock(&irq_controller_lock);
342         return;
343 }
344
345 static void enable_piix4_virtual_irq(unsigned int irq)
346 {
347         /*
348          * assumes this irq is one of the legacy devices
349          */
350
351         unsigned int mask = inb(0x21);
352         mask &= ~(1 << irq);
353         outb(mask, 0x21);
354         enable_cobalt_irq(irq);
355 }
356
357 /*
358  * assumes this irq is one of the legacy devices
359  */
360 static void disable_piix4_virtual_irq(unsigned int irq)
361 {
362         unsigned int mask;
363
364         disable_cobalt_irq(irq);
365
366         mask = inb(0x21);
367         mask &= ~(1 << irq);
368         outb(mask, 0x21);
369 }
370
371 static struct irqaction master_action =
372                 { no_action, 0, 0, "PIIX4-8259", NULL, NULL };
373
374 void init_VISWS_APIC_irqs(void)
375 {
376         int i;
377
378         for (i = 0; i < 16; i++) {
379                 irq_desc[i].status = IRQ_DISABLED;
380                 irq_desc[i].action = 0;
381                 irq_desc[i].depth = 1;
382
383                 /*
384                  * Cobalt IRQs are mapped to standard ISA
385                  * interrupt vectors:
386                  */
387                 switch (i) {
388                         /*
389                          * Only CO_IRQ_8259 will be raised
390                          * externally.
391                          */
392                 case CO_IRQ_8259:
393                         irq_desc[i].handler = &piix4_master_irq_type;
394                         break;
395                 case CO_IRQ_FLOPPY:
396                 case CO_IRQ_PARLL:
397                         irq_desc[i].handler = &piix4_virtual_irq_type;
398                         break;
399                 default:
400                         irq_desc[i].handler = &cobalt_irq_type;
401                         break;
402                 }
403         }
404
405         /*
406          * The master interrupt is always present:
407          */
408         setup_x86_irq(CO_IRQ_8259, &master_action);
409 }
410