]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * OSS handling | |
3 | * Written by Joshua M. Thompson (funaho@jurai.org) | |
4 | * | |
5 | * | |
6 | * This chip is used in the IIfx in place of VIA #2. It acts like a fancy | |
7 | * VIA chip with prorammable interrupt levels. | |
8 | * | |
9 | * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some | |
10 | * recent insights into OSS operational details. | |
0c79cf6a | 11 | * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped |
1da177e4 LT |
12 | * to mostly match the A/UX interrupt scheme supported on the |
13 | * VIA side. Also added support for enabling the ISM irq again | |
14 | * since we now have a functional IOP manager. | |
15 | */ | |
16 | ||
17 | #include <linux/types.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/mm.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/init.h> | |
ddc7fd25 GU |
22 | #ifdef CONFIG_GENERIC_HARDIRQS |
23 | #include <linux/irq.h> | |
24 | #endif | |
1da177e4 LT |
25 | |
26 | #include <asm/bootinfo.h> | |
1da177e4 LT |
27 | #include <asm/macintosh.h> |
28 | #include <asm/macints.h> | |
29 | #include <asm/mac_via.h> | |
30 | #include <asm/mac_oss.h> | |
31 | ||
32 | int oss_present; | |
33 | volatile struct mac_oss *oss; | |
34 | ||
8dfbdf4a AB |
35 | static irqreturn_t oss_irq(int, void *); |
36 | static irqreturn_t oss_nubus_irq(int, void *); | |
1da177e4 | 37 | |
2850bc27 | 38 | extern irqreturn_t via1_irq(int, void *); |
1da177e4 LT |
39 | |
40 | /* | |
41 | * Initialize the OSS | |
42 | * | |
43 | * The OSS "detection" code is actually in via_init() which is always called | |
44 | * before us. Thus we can count on oss_present being valid on entry. | |
45 | */ | |
46 | ||
47 | void __init oss_init(void) | |
48 | { | |
49 | int i; | |
50 | ||
51 | if (!oss_present) return; | |
52 | ||
53 | oss = (struct mac_oss *) OSS_BASE; | |
54 | ||
55 | /* Disable all interrupts. Unlike a VIA it looks like we */ | |
56 | /* do this by setting the source's interrupt level to zero. */ | |
57 | ||
58 | for (i = 0; i <= OSS_NUM_SOURCES; i++) { | |
59 | oss->irq_level[i] = OSS_IRQLEV_DISABLED; | |
60 | } | |
61 | /* If we disable VIA1 here, we never really handle it... */ | |
62 | oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; | |
63 | } | |
64 | ||
65 | /* | |
66 | * Register the OSS and NuBus interrupt dispatchers. | |
67 | */ | |
68 | ||
69 | void __init oss_register_interrupts(void) | |
70 | { | |
5a239453 | 71 | if (request_irq(OSS_IRQLEV_SCSI, oss_irq, 0, "scsi", (void *)oss)) |
92c3dd15 | 72 | pr_err("Couldn't register %s interrupt\n", "scsi"); |
5a239453 GU |
73 | if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, 0, "nubus", |
74 | (void *)oss)) | |
92c3dd15 | 75 | pr_err("Couldn't register %s interrupt\n", "nubus"); |
5a239453 | 76 | if (request_irq(OSS_IRQLEV_SOUND, oss_irq, 0, "sound", (void *)oss)) |
92c3dd15 | 77 | pr_err("Couldn't register %s interrupt\n", "sound"); |
5a239453 | 78 | if (request_irq(OSS_IRQLEV_VIA1, via1_irq, 0, "via1", (void *)via1)) |
92c3dd15 | 79 | pr_err("Couldn't register %s interrupt\n", "via1"); |
1da177e4 LT |
80 | } |
81 | ||
82 | /* | |
83 | * Initialize OSS for Nubus access | |
84 | */ | |
85 | ||
86 | void __init oss_nubus_init(void) | |
87 | { | |
88 | } | |
89 | ||
90 | /* | |
91 | * Handle miscellaneous OSS interrupts. Right now that's just sound | |
92 | * and SCSI; everything else is routed to its own autovector IRQ. | |
93 | */ | |
94 | ||
8dfbdf4a | 95 | static irqreturn_t oss_irq(int irq, void *dev_id) |
1da177e4 LT |
96 | { |
97 | int events; | |
98 | ||
99 | events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); | |
100 | if (!events) | |
101 | return IRQ_NONE; | |
102 | ||
103 | #ifdef DEBUG_IRQS | |
104 | if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { | |
105 | printk("oss_irq: irq %d events = 0x%04X\n", irq, | |
106 | (int) oss->irq_pending); | |
107 | } | |
108 | #endif | |
109 | /* FIXME: how do you clear a pending IRQ? */ | |
110 | ||
111 | if (events & OSS_IP_SOUND) { | |
1da177e4 | 112 | oss->irq_pending &= ~OSS_IP_SOUND; |
647b804c | 113 | /* FIXME: call sound handler */ |
1da177e4 | 114 | } else if (events & OSS_IP_SCSI) { |
1da177e4 | 115 | oss->irq_pending &= ~OSS_IP_SCSI; |
1425df87 | 116 | generic_handle_irq(IRQ_MAC_SCSI); |
1da177e4 LT |
117 | } else { |
118 | /* FIXME: error check here? */ | |
119 | } | |
120 | return IRQ_HANDLED; | |
121 | } | |
122 | ||
123 | /* | |
124 | * Nubus IRQ handler, OSS style | |
125 | * | |
126 | * Unlike the VIA/RBV this is on its own autovector interrupt level. | |
127 | */ | |
128 | ||
8dfbdf4a | 129 | static irqreturn_t oss_nubus_irq(int irq, void *dev_id) |
1da177e4 LT |
130 | { |
131 | int events, irq_bit, i; | |
132 | ||
133 | events = oss->irq_pending & OSS_IP_NUBUS; | |
134 | if (!events) | |
135 | return IRQ_NONE; | |
136 | ||
137 | #ifdef DEBUG_NUBUS_INT | |
138 | if (console_loglevel > 7) { | |
139 | printk("oss_nubus_irq: events = 0x%04X\n", events); | |
140 | } | |
141 | #endif | |
142 | /* There are only six slots on the OSS, not seven */ | |
143 | ||
67dfb153 FT |
144 | i = 6; |
145 | irq_bit = 0x40; | |
146 | do { | |
147 | --i; | |
148 | irq_bit >>= 1; | |
1da177e4 | 149 | if (events & irq_bit) { |
1da177e4 | 150 | oss->irq_pending &= ~irq_bit; |
1425df87 | 151 | generic_handle_irq(NUBUS_SOURCE_BASE + i); |
1da177e4 | 152 | } |
67dfb153 | 153 | } while(events & (irq_bit - 1)); |
1da177e4 LT |
154 | return IRQ_HANDLED; |
155 | } | |
156 | ||
157 | /* | |
158 | * Enable an OSS interrupt | |
159 | * | |
160 | * It looks messy but it's rather straightforward. The switch() statement | |
161 | * just maps the machspec interrupt numbers to the right OSS interrupt | |
162 | * source (if the OSS handles that interrupt) and then sets the interrupt | |
163 | * level for that source to nonzero, thus enabling the interrupt. | |
164 | */ | |
165 | ||
166 | void oss_irq_enable(int irq) { | |
167 | #ifdef DEBUG_IRQUSE | |
168 | printk("oss_irq_enable(%d)\n", irq); | |
169 | #endif | |
170 | switch(irq) { | |
80614e5a | 171 | case IRQ_MAC_SCC: |
1da177e4 LT |
172 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; |
173 | break; | |
174 | case IRQ_MAC_ADB: | |
175 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; | |
176 | break; | |
177 | case IRQ_MAC_SCSI: | |
178 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; | |
179 | break; | |
180 | case IRQ_NUBUS_9: | |
181 | case IRQ_NUBUS_A: | |
182 | case IRQ_NUBUS_B: | |
183 | case IRQ_NUBUS_C: | |
184 | case IRQ_NUBUS_D: | |
185 | case IRQ_NUBUS_E: | |
186 | irq -= NUBUS_SOURCE_BASE; | |
187 | oss->irq_level[irq] = OSS_IRQLEV_NUBUS; | |
188 | break; | |
189 | #ifdef DEBUG_IRQUSE | |
190 | default: | |
f85e7cdc | 191 | printk("%s unknown irq %d\n", __func__, irq); |
1da177e4 LT |
192 | break; |
193 | #endif | |
194 | } | |
195 | } | |
196 | ||
197 | /* | |
198 | * Disable an OSS interrupt | |
199 | * | |
200 | * Same as above except we set the source's interrupt level to zero, | |
201 | * to disable the interrupt. | |
202 | */ | |
203 | ||
204 | void oss_irq_disable(int irq) { | |
205 | #ifdef DEBUG_IRQUSE | |
206 | printk("oss_irq_disable(%d)\n", irq); | |
207 | #endif | |
208 | switch(irq) { | |
80614e5a | 209 | case IRQ_MAC_SCC: |
1da177e4 LT |
210 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; |
211 | break; | |
212 | case IRQ_MAC_ADB: | |
213 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; | |
214 | break; | |
215 | case IRQ_MAC_SCSI: | |
216 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; | |
217 | break; | |
218 | case IRQ_NUBUS_9: | |
219 | case IRQ_NUBUS_A: | |
220 | case IRQ_NUBUS_B: | |
221 | case IRQ_NUBUS_C: | |
222 | case IRQ_NUBUS_D: | |
223 | case IRQ_NUBUS_E: | |
224 | irq -= NUBUS_SOURCE_BASE; | |
225 | oss->irq_level[irq] = OSS_IRQLEV_DISABLED; | |
226 | break; | |
227 | #ifdef DEBUG_IRQUSE | |
228 | default: | |
f85e7cdc | 229 | printk("%s unknown irq %d\n", __func__, irq); |
1da177e4 LT |
230 | break; |
231 | #endif | |
232 | } | |
233 | } | |
234 | ||
235 | /* | |
236 | * Clear an OSS interrupt | |
237 | * | |
238 | * Not sure if this works or not but it's the only method I could | |
239 | * think of based on the contents of the mac_oss structure. | |
240 | */ | |
241 | ||
242 | void oss_irq_clear(int irq) { | |
243 | /* FIXME: how to do this on OSS? */ | |
244 | switch(irq) { | |
80614e5a | 245 | case IRQ_MAC_SCC: |
1da177e4 LT |
246 | oss->irq_pending &= ~OSS_IP_IOPSCC; |
247 | break; | |
248 | case IRQ_MAC_ADB: | |
249 | oss->irq_pending &= ~OSS_IP_IOPISM; | |
250 | break; | |
251 | case IRQ_MAC_SCSI: | |
252 | oss->irq_pending &= ~OSS_IP_SCSI; | |
253 | break; | |
254 | case IRQ_NUBUS_9: | |
255 | case IRQ_NUBUS_A: | |
256 | case IRQ_NUBUS_B: | |
257 | case IRQ_NUBUS_C: | |
258 | case IRQ_NUBUS_D: | |
259 | case IRQ_NUBUS_E: | |
260 | irq -= NUBUS_SOURCE_BASE; | |
261 | oss->irq_pending &= ~(1 << irq); | |
262 | break; | |
263 | } | |
264 | } | |
265 | ||
266 | /* | |
267 | * Check to see if a specific OSS interrupt is pending | |
268 | */ | |
269 | ||
270 | int oss_irq_pending(int irq) | |
271 | { | |
272 | switch(irq) { | |
80614e5a | 273 | case IRQ_MAC_SCC: |
1da177e4 LT |
274 | return oss->irq_pending & OSS_IP_IOPSCC; |
275 | break; | |
276 | case IRQ_MAC_ADB: | |
277 | return oss->irq_pending & OSS_IP_IOPISM; | |
278 | break; | |
279 | case IRQ_MAC_SCSI: | |
280 | return oss->irq_pending & OSS_IP_SCSI; | |
281 | break; | |
282 | case IRQ_NUBUS_9: | |
283 | case IRQ_NUBUS_A: | |
284 | case IRQ_NUBUS_B: | |
285 | case IRQ_NUBUS_C: | |
286 | case IRQ_NUBUS_D: | |
287 | case IRQ_NUBUS_E: | |
288 | irq -= NUBUS_SOURCE_BASE; | |
289 | return oss->irq_pending & (1 << irq); | |
290 | break; | |
291 | } | |
292 | return 0; | |
293 | } |