m68k/mac: Optimize interrupts using chain handlers
[linux-3.10.git] / arch / m68k / mac / baboon.c
1 /*
2  * Baboon Custom IC Management
3  *
4  * The Baboon custom IC controls the IDE, PCMCIA and media bay on the
5  * PowerBook 190. It multiplexes multiple interrupt sources onto the
6  * Nubus slot $C interrupt.
7  */
8
9 #include <linux/types.h>
10 #include <linux/kernel.h>
11 #include <linux/mm.h>
12 #include <linux/delay.h>
13 #include <linux/init.h>
14 #ifdef CONFIG_GENERIC_HARDIRQS
15 #include <linux/irq.h>
16 #endif
17
18 #include <asm/traps.h>
19 #include <asm/bootinfo.h>
20 #include <asm/macintosh.h>
21 #include <asm/macints.h>
22 #include <asm/mac_baboon.h>
23
24 /* #define DEBUG_IRQS */
25
26 extern void mac_enable_irq(unsigned int);
27 extern void mac_disable_irq(unsigned int);
28
29 int baboon_present;
30 static volatile struct baboon *baboon;
31 static unsigned char baboon_disabled;
32
33 #if 0
34 extern int macide_ack_intr(struct ata_channel *);
35 #endif
36
37 /*
38  * Baboon initialization.
39  */
40
41 void __init baboon_init(void)
42 {
43         if (macintosh_config->ident != MAC_MODEL_PB190) {
44                 baboon = NULL;
45                 baboon_present = 0;
46                 return;
47         }
48
49         baboon = (struct baboon *) BABOON_BASE;
50         baboon_present = 1;
51
52         printk("Baboon detected at %p\n", baboon);
53 }
54
55 /*
56  * Baboon interrupt handler. This works a lot like a VIA.
57  */
58
59 #ifdef CONFIG_GENERIC_HARDIRQS
60 static void baboon_irq(unsigned int irq, struct irq_desc *desc)
61 {
62         int irq_bit, irq_num;
63         unsigned char events;
64
65 #ifdef DEBUG_IRQS
66         printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X\n",
67                 (uint) baboon->mb_control, (uint) baboon->mb_ifr,
68                 (uint) baboon->mb_status);
69 #endif
70
71         events = baboon->mb_ifr & 0x07;
72         if (!events)
73                 return;
74
75         irq_num = IRQ_BABOON_0;
76         irq_bit = 1;
77         do {
78                 if (events & irq_bit) {
79                         baboon->mb_ifr &= ~irq_bit;
80                         generic_handle_irq(irq_num);
81                 }
82                 irq_bit <<= 1;
83                 irq_num++;
84         } while(events >= irq_bit);
85 #if 0
86         if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
87         /* for now we need to smash all interrupts */
88         baboon->mb_ifr &= ~events;
89 #endif
90 }
91 #else
92 static irqreturn_t baboon_irq(int irq, void *dev_id)
93 {
94         int irq_bit, irq_num;
95         unsigned char events;
96
97 #ifdef DEBUG_IRQS
98         printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X\n",
99                 (uint) baboon->mb_control, (uint) baboon->mb_ifr,
100                 (uint) baboon->mb_status);
101 #endif
102
103         if (!(events = baboon->mb_ifr & 0x07))
104                 return IRQ_NONE;
105
106         irq_num = IRQ_BABOON_0;
107         irq_bit = 1;
108         do {
109                 if (events & irq_bit) {
110                         baboon->mb_ifr &= ~irq_bit;
111                         generic_handle_irq(irq_num);
112                 }
113                 irq_bit <<= 1;
114                 irq_num++;
115         } while(events >= irq_bit);
116 #if 0
117         if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
118         /* for now we need to smash all interrupts */
119         baboon->mb_ifr &= ~events;
120 #endif
121         return IRQ_HANDLED;
122 }
123 #endif
124
125 /*
126  * Register the Baboon interrupt dispatcher on nubus slot $C.
127  */
128
129 void __init baboon_register_interrupts(void)
130 {
131         baboon_disabled = 0;
132 #ifdef CONFIG_GENERIC_HARDIRQS
133         irq_set_chained_handler(IRQ_NUBUS_C, baboon_irq);
134 #else
135         if (request_irq(IRQ_NUBUS_C, baboon_irq, 0, "baboon", (void *)baboon))
136                 pr_err("Couldn't register baboon interrupt\n");
137 #endif
138 }
139
140 /*
141  * The means for masking individual baboon interrupts remains a mystery, so
142  * enable the umbrella interrupt only when no baboon interrupt is disabled.
143  */
144
145 void baboon_irq_enable(int irq)
146 {
147         int irq_idx = IRQ_IDX(irq);
148
149 #ifdef DEBUG_IRQUSE
150         printk("baboon_irq_enable(%d)\n", irq);
151 #endif
152
153         baboon_disabled &= ~(1 << irq_idx);
154         if (!baboon_disabled)
155                 mac_enable_irq(IRQ_NUBUS_C);
156 }
157
158 void baboon_irq_disable(int irq)
159 {
160         int irq_idx = IRQ_IDX(irq);
161
162 #ifdef DEBUG_IRQUSE
163         printk("baboon_irq_disable(%d)\n", irq);
164 #endif
165
166         baboon_disabled |= 1 << irq_idx;
167         if (baboon_disabled)
168                 mac_disable_irq(IRQ_NUBUS_C);
169 }
170
171 void baboon_irq_clear(int irq)
172 {
173         int irq_idx = IRQ_IDX(irq);
174
175         baboon->mb_ifr &= ~(1 << irq_idx);
176 }
177
178 int baboon_irq_pending(int irq)
179 {
180         int irq_idx = IRQ_IDX(irq);
181
182         return baboon->mb_ifr & (1 << irq_idx);
183 }