Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/m68k/mac/oss.c
10817 views
1
/*
2
* OSS handling
3
* Written by Joshua M. Thompson ([email protected])
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.
11
* 990610 (jmt) - Now taking full 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.
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>
22
23
#include <asm/bootinfo.h>
24
#include <asm/macintosh.h>
25
#include <asm/macints.h>
26
#include <asm/mac_via.h>
27
#include <asm/mac_oss.h>
28
29
int oss_present;
30
volatile struct mac_oss *oss;
31
32
static irqreturn_t oss_irq(int, void *);
33
static irqreturn_t oss_nubus_irq(int, void *);
34
35
extern irqreturn_t via1_irq(int, void *);
36
37
/*
38
* Initialize the OSS
39
*
40
* The OSS "detection" code is actually in via_init() which is always called
41
* before us. Thus we can count on oss_present being valid on entry.
42
*/
43
44
void __init oss_init(void)
45
{
46
int i;
47
48
if (!oss_present) return;
49
50
oss = (struct mac_oss *) OSS_BASE;
51
52
/* Disable all interrupts. Unlike a VIA it looks like we */
53
/* do this by setting the source's interrupt level to zero. */
54
55
for (i = 0; i <= OSS_NUM_SOURCES; i++) {
56
oss->irq_level[i] = OSS_IRQLEV_DISABLED;
57
}
58
/* If we disable VIA1 here, we never really handle it... */
59
oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
60
}
61
62
/*
63
* Register the OSS and NuBus interrupt dispatchers.
64
*/
65
66
void __init oss_register_interrupts(void)
67
{
68
if (request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
69
"scsi", (void *) oss))
70
pr_err("Couldn't register %s interrupt\n", "scsi");
71
if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
72
"nubus", (void *) oss))
73
pr_err("Couldn't register %s interrupt\n", "nubus");
74
if (request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
75
"sound", (void *) oss))
76
pr_err("Couldn't register %s interrupt\n", "sound");
77
if (request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
78
"via1", (void *) via1))
79
pr_err("Couldn't register %s interrupt\n", "via1");
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
95
static irqreturn_t oss_irq(int irq, void *dev_id)
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) {
112
oss->irq_pending &= ~OSS_IP_SOUND;
113
/* FIXME: call sound handler */
114
} else if (events & OSS_IP_SCSI) {
115
oss->irq_pending &= ~OSS_IP_SCSI;
116
m68k_handle_int(IRQ_MAC_SCSI);
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
129
static irqreturn_t oss_nubus_irq(int irq, void *dev_id)
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
144
i = 6;
145
irq_bit = 0x40;
146
do {
147
--i;
148
irq_bit >>= 1;
149
if (events & irq_bit) {
150
oss->irq_pending &= ~irq_bit;
151
m68k_handle_int(NUBUS_SOURCE_BASE + i);
152
}
153
} while(events & (irq_bit - 1));
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) {
171
case IRQ_MAC_SCC:
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:
191
printk("%s unknown irq %d\n", __func__, irq);
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) {
209
case IRQ_MAC_SCC:
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:
229
printk("%s unknown irq %d\n", __func__, irq);
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) {
245
case IRQ_MAC_SCC:
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) {
273
case IRQ_MAC_SCC:
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
}
294
295