* added 0.99 linux version
[mascara-docs.git] / i386 / linux / linux-2.3.21 / arch / m68k / mac / oss.c
blob442d72802b6b7f6b3ae65bf2266b6862ddef0bb2
1 /*
2 * OSS handling
3 * Written by Joshua M. Thompson (funaho@jurai.org)
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.
9 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
10 * recent insights into OSS operational details.
11 * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped
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.
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>
23 #include <asm/bootinfo.h>
24 #include <asm/machw.h>
25 #include <asm/macintosh.h>
26 #include <asm/macints.h>
27 #include <asm/mac_via.h>
28 #include <asm/mac_oss.h>
30 int oss_present;
31 volatile struct mac_oss *oss;
33 void oss_irq(int, void *, struct pt_regs *);
34 void oss_nubus_irq(int, void *, struct pt_regs *);
36 extern void via1_irq(int, void *, struct pt_regs *);
37 extern void mac_SCC_handler(int, void *, struct pt_regs *);
38 extern int console_loglevel;
41 * Initialize the OSS
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.
47 void __init oss_init(void)
49 int i;
51 if (!oss_present) return;
53 oss = (struct mac_oss *) OSS_BASE;
55 /* Disable all interrupts. Unlike a VIA it looks like we */
56 /* do this by setting the source's interrupt level to zero. */
58 for (i = 0; i <= OSS_NUM_SOURCES; i++) {
59 oss->irq_level[i] = OSS_IRQLEV_DISABLED;
61 /* If we disable VIA1 here, we never really handle it... */
62 oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
66 * Register the OSS and NuBus interrupt dispatchers.
69 void __init oss_register_interrupts(void)
71 request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
72 "OSS SCSI Dispatch", (void *) oss);
73 request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK,
74 "SCC Dispatch", mac_SCC_handler);
75 request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
76 "Nubus Dispatch", (void *) oss);
77 request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
78 "OSS Sound Dispatch", (void *) oss);
79 request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
80 "VIA1 Dispatch", (void *) via1);
84 * Initialize OSS for Nubus access
87 void __init oss_nubus_init(void)
92 * Turn off the power via the ROM control register
94 * FIXME: not sure how this is supposed to work exactly...
97 void oss_poweroff(void)
99 oss->rom_ctrl = OSS_POWEROFF;
101 /* We should never make it this far... */
103 printk ("It is now safe to switch off your machine.\n");
104 while(1);
108 * Handle miscellaneous OSS interrupts. Right now that's just sound
109 * and SCSI; everything else is routed to its own autovector IRQ.
112 void oss_irq(int irq, void *dev_id, struct pt_regs *regs)
114 int events;
116 events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
117 if (!events) return;
119 #ifdef DEBUG_IRQS
120 if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
121 printk("oss_irq: irq %d events = 0x%04X\n", irq,
122 (int) oss->irq_pending);
124 #endif
125 /* FIXME: how do you clear a pending IRQ? */
127 if (events & OSS_IP_SOUND) {
128 /* FIXME: call sound handler */
129 oss->irq_pending &= ~OSS_IP_SOUND;
130 } else if (events & OSS_IP_SCSI) {
131 oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
132 mac_do_irq_list(IRQ_MAC_SCSI, regs);
133 oss->irq_pending &= ~OSS_IP_SCSI;
134 oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
135 } else {
136 /* FIXME: error check here? */
141 * Nubus IRQ handler, OSS style
143 * Unlike the VIA/RBV this is on its own autovector interupt level.
146 void oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
148 int events, irq_bit, i;
150 events = oss->irq_pending & OSS_IP_NUBUS;
151 if (!events) return;
153 #ifdef DEBUG_NUBUS_INT
154 if (console_loglevel > 7) {
155 printk("oss_nubus_irq: events = 0x%04X\n", events);
157 #endif
158 /* There are only six slots on the OSS, not seven */
160 for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) {
161 if (events & irq_bit) {
162 oss->irq_level[i] = OSS_IRQLEV_DISABLED;
163 mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
164 oss->irq_pending &= ~irq_bit;
165 oss->irq_level[i] = OSS_IRQLEV_NUBUS;
171 * Enable an OSS interrupt
173 * It looks messy but it's rather straightforward. The switch() statement
174 * just maps the machspec interrupt numbers to the right OSS interrupt
175 * source (if the OSS handles that interrupt) and then sets the interrupt
176 * level for that source to nonzero, thus enabling the interrupt.
179 void oss_irq_enable(int irq) {
180 #ifdef DEBUG_IRQUSE
181 printk("oss_irq_enable(%d)\n", irq);
182 #endif
183 switch(irq) {
184 case IRQ_SCC:
185 case IRQ_SCCA:
186 case IRQ_SCCB:
187 oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
188 break;
189 case IRQ_MAC_ADB:
190 oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
191 break;
192 case IRQ_MAC_SCSI:
193 oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
194 break;
195 case IRQ_NUBUS_9:
196 case IRQ_NUBUS_A:
197 case IRQ_NUBUS_B:
198 case IRQ_NUBUS_C:
199 case IRQ_NUBUS_D:
200 case IRQ_NUBUS_E:
201 irq -= NUBUS_SOURCE_BASE;
202 oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
203 break;
204 #ifdef DEBUG_IRQUSE
205 default:
206 printk("%s unknown irq %d\n",__FUNCTION__, irq);
207 break;
208 #endif
213 * Disable an OSS interrupt
215 * Same as above except we set the source's interrupt level to zero,
216 * to disable the interrupt.
219 void oss_irq_disable(int irq) {
220 #ifdef DEBUG_IRQUSE
221 printk("oss_irq_disable(%d)\n", irq);
222 #endif
223 switch(irq) {
224 case IRQ_SCC:
225 case IRQ_SCCA:
226 case IRQ_SCCB:
227 oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED;
228 break;
229 case IRQ_MAC_ADB:
230 oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED;
231 break;
232 case IRQ_MAC_SCSI:
233 oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
234 break;
235 case IRQ_NUBUS_9:
236 case IRQ_NUBUS_A:
237 case IRQ_NUBUS_B:
238 case IRQ_NUBUS_C:
239 case IRQ_NUBUS_D:
240 case IRQ_NUBUS_E:
241 irq -= NUBUS_SOURCE_BASE;
242 oss->irq_level[irq] = OSS_IRQLEV_DISABLED;
243 break;
244 #ifdef DEBUG_IRQUSE
245 default:
246 printk("%s unknown irq %d\n", __FUNCTION__, irq);
247 break;
248 #endif
253 * Clear an OSS interrupt
255 * Not sure if this works or not but it's the only method I could
256 * think of based on the contents of the mac_oss structure.
259 void oss_irq_clear(int irq) {
260 /* FIXME: how to do this on OSS? */
261 switch(irq) {
262 case IRQ_SCC:
263 case IRQ_SCCA:
264 case IRQ_SCCB:
265 oss->irq_pending &= ~OSS_IP_IOPSCC;
266 break;
267 case IRQ_MAC_ADB:
268 oss->irq_pending &= ~OSS_IP_IOPISM;
269 break;
270 case IRQ_MAC_SCSI:
271 oss->irq_pending &= ~OSS_IP_SCSI;
272 break;
273 case IRQ_NUBUS_9:
274 case IRQ_NUBUS_A:
275 case IRQ_NUBUS_B:
276 case IRQ_NUBUS_C:
277 case IRQ_NUBUS_D:
278 case IRQ_NUBUS_E:
279 irq -= NUBUS_SOURCE_BASE;
280 oss->irq_pending &= ~(1 << irq);
281 break;
286 * Check to see if a specific OSS interrupt is pending
289 int oss_irq_pending(int irq)
291 switch(irq) {
292 case IRQ_SCC:
293 case IRQ_SCCA:
294 case IRQ_SCCB:
295 return oss->irq_pending & OSS_IP_IOPSCC;
296 break;
297 case IRQ_MAC_ADB:
298 return oss->irq_pending & OSS_IP_IOPISM;
299 break;
300 case IRQ_MAC_SCSI:
301 return oss->irq_pending & OSS_IP_SCSI;
302 break;
303 case IRQ_NUBUS_9:
304 case IRQ_NUBUS_A:
305 case IRQ_NUBUS_B:
306 case IRQ_NUBUS_C:
307 case IRQ_NUBUS_D:
308 case IRQ_NUBUS_E:
309 irq -= NUBUS_SOURCE_BASE;
310 return oss->irq_pending & (1 << irq);
311 break;
313 return 0;