diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68k/mac/oss.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/m68k/mac/oss.c')
-rw-r--r-- | arch/m68k/mac/oss.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c new file mode 100644 index 000000000000..333547692724 --- /dev/null +++ b/arch/m68k/mac/oss.c | |||
@@ -0,0 +1,301 @@ | |||
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. | ||
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. | ||
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/machw.h> | ||
25 | #include <asm/macintosh.h> | ||
26 | #include <asm/macints.h> | ||
27 | #include <asm/mac_via.h> | ||
28 | #include <asm/mac_oss.h> | ||
29 | |||
30 | int oss_present; | ||
31 | volatile struct mac_oss *oss; | ||
32 | |||
33 | irqreturn_t oss_irq(int, void *, struct pt_regs *); | ||
34 | irqreturn_t oss_nubus_irq(int, void *, struct pt_regs *); | ||
35 | |||
36 | extern irqreturn_t via1_irq(int, void *, struct pt_regs *); | ||
37 | extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *); | ||
38 | |||
39 | /* | ||
40 | * Initialize the OSS | ||
41 | * | ||
42 | * The OSS "detection" code is actually in via_init() which is always called | ||
43 | * before us. Thus we can count on oss_present being valid on entry. | ||
44 | */ | ||
45 | |||
46 | void __init oss_init(void) | ||
47 | { | ||
48 | int i; | ||
49 | |||
50 | if (!oss_present) return; | ||
51 | |||
52 | oss = (struct mac_oss *) OSS_BASE; | ||
53 | |||
54 | /* Disable all interrupts. Unlike a VIA it looks like we */ | ||
55 | /* do this by setting the source's interrupt level to zero. */ | ||
56 | |||
57 | for (i = 0; i <= OSS_NUM_SOURCES; i++) { | ||
58 | oss->irq_level[i] = OSS_IRQLEV_DISABLED; | ||
59 | } | ||
60 | /* If we disable VIA1 here, we never really handle it... */ | ||
61 | oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Register the OSS and NuBus interrupt dispatchers. | ||
66 | */ | ||
67 | |||
68 | void __init oss_register_interrupts(void) | ||
69 | { | ||
70 | cpu_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, | ||
71 | "scsi", (void *) oss); | ||
72 | cpu_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK, | ||
73 | "scc", mac_scc_dispatch); | ||
74 | cpu_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, | ||
75 | "nubus", (void *) oss); | ||
76 | cpu_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, | ||
77 | "sound", (void *) oss); | ||
78 | cpu_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, | ||
79 | "via1", (void *) 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 | irqreturn_t oss_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
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 | /* FIXME: call sound handler */ | ||
113 | oss->irq_pending &= ~OSS_IP_SOUND; | ||
114 | } else if (events & OSS_IP_SCSI) { | ||
115 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; | ||
116 | mac_do_irq_list(IRQ_MAC_SCSI, regs); | ||
117 | oss->irq_pending &= ~OSS_IP_SCSI; | ||
118 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; | ||
119 | } else { | ||
120 | /* FIXME: error check here? */ | ||
121 | } | ||
122 | return IRQ_HANDLED; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * Nubus IRQ handler, OSS style | ||
127 | * | ||
128 | * Unlike the VIA/RBV this is on its own autovector interrupt level. | ||
129 | */ | ||
130 | |||
131 | irqreturn_t oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
132 | { | ||
133 | int events, irq_bit, i; | ||
134 | |||
135 | events = oss->irq_pending & OSS_IP_NUBUS; | ||
136 | if (!events) | ||
137 | return IRQ_NONE; | ||
138 | |||
139 | #ifdef DEBUG_NUBUS_INT | ||
140 | if (console_loglevel > 7) { | ||
141 | printk("oss_nubus_irq: events = 0x%04X\n", events); | ||
142 | } | ||
143 | #endif | ||
144 | /* There are only six slots on the OSS, not seven */ | ||
145 | |||
146 | for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) { | ||
147 | if (events & irq_bit) { | ||
148 | oss->irq_level[i] = OSS_IRQLEV_DISABLED; | ||
149 | mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); | ||
150 | oss->irq_pending &= ~irq_bit; | ||
151 | oss->irq_level[i] = OSS_IRQLEV_NUBUS; | ||
152 | } | ||
153 | } | ||
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_SCC: | ||
172 | case IRQ_SCCA: | ||
173 | case IRQ_SCCB: | ||
174 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; | ||
175 | break; | ||
176 | case IRQ_MAC_ADB: | ||
177 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; | ||
178 | break; | ||
179 | case IRQ_MAC_SCSI: | ||
180 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; | ||
181 | break; | ||
182 | case IRQ_NUBUS_9: | ||
183 | case IRQ_NUBUS_A: | ||
184 | case IRQ_NUBUS_B: | ||
185 | case IRQ_NUBUS_C: | ||
186 | case IRQ_NUBUS_D: | ||
187 | case IRQ_NUBUS_E: | ||
188 | irq -= NUBUS_SOURCE_BASE; | ||
189 | oss->irq_level[irq] = OSS_IRQLEV_NUBUS; | ||
190 | break; | ||
191 | #ifdef DEBUG_IRQUSE | ||
192 | default: | ||
193 | printk("%s unknown irq %d\n",__FUNCTION__, irq); | ||
194 | break; | ||
195 | #endif | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Disable an OSS interrupt | ||
201 | * | ||
202 | * Same as above except we set the source's interrupt level to zero, | ||
203 | * to disable the interrupt. | ||
204 | */ | ||
205 | |||
206 | void oss_irq_disable(int irq) { | ||
207 | #ifdef DEBUG_IRQUSE | ||
208 | printk("oss_irq_disable(%d)\n", irq); | ||
209 | #endif | ||
210 | switch(irq) { | ||
211 | case IRQ_SCC: | ||
212 | case IRQ_SCCA: | ||
213 | case IRQ_SCCB: | ||
214 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; | ||
215 | break; | ||
216 | case IRQ_MAC_ADB: | ||
217 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; | ||
218 | break; | ||
219 | case IRQ_MAC_SCSI: | ||
220 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; | ||
221 | break; | ||
222 | case IRQ_NUBUS_9: | ||
223 | case IRQ_NUBUS_A: | ||
224 | case IRQ_NUBUS_B: | ||
225 | case IRQ_NUBUS_C: | ||
226 | case IRQ_NUBUS_D: | ||
227 | case IRQ_NUBUS_E: | ||
228 | irq -= NUBUS_SOURCE_BASE; | ||
229 | oss->irq_level[irq] = OSS_IRQLEV_DISABLED; | ||
230 | break; | ||
231 | #ifdef DEBUG_IRQUSE | ||
232 | default: | ||
233 | printk("%s unknown irq %d\n", __FUNCTION__, irq); | ||
234 | break; | ||
235 | #endif | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Clear an OSS interrupt | ||
241 | * | ||
242 | * Not sure if this works or not but it's the only method I could | ||
243 | * think of based on the contents of the mac_oss structure. | ||
244 | */ | ||
245 | |||
246 | void oss_irq_clear(int irq) { | ||
247 | /* FIXME: how to do this on OSS? */ | ||
248 | switch(irq) { | ||
249 | case IRQ_SCC: | ||
250 | case IRQ_SCCA: | ||
251 | case IRQ_SCCB: | ||
252 | oss->irq_pending &= ~OSS_IP_IOPSCC; | ||
253 | break; | ||
254 | case IRQ_MAC_ADB: | ||
255 | oss->irq_pending &= ~OSS_IP_IOPISM; | ||
256 | break; | ||
257 | case IRQ_MAC_SCSI: | ||
258 | oss->irq_pending &= ~OSS_IP_SCSI; | ||
259 | break; | ||
260 | case IRQ_NUBUS_9: | ||
261 | case IRQ_NUBUS_A: | ||
262 | case IRQ_NUBUS_B: | ||
263 | case IRQ_NUBUS_C: | ||
264 | case IRQ_NUBUS_D: | ||
265 | case IRQ_NUBUS_E: | ||
266 | irq -= NUBUS_SOURCE_BASE; | ||
267 | oss->irq_pending &= ~(1 << irq); | ||
268 | break; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Check to see if a specific OSS interrupt is pending | ||
274 | */ | ||
275 | |||
276 | int oss_irq_pending(int irq) | ||
277 | { | ||
278 | switch(irq) { | ||
279 | case IRQ_SCC: | ||
280 | case IRQ_SCCA: | ||
281 | case IRQ_SCCB: | ||
282 | return oss->irq_pending & OSS_IP_IOPSCC; | ||
283 | break; | ||
284 | case IRQ_MAC_ADB: | ||
285 | return oss->irq_pending & OSS_IP_IOPISM; | ||
286 | break; | ||
287 | case IRQ_MAC_SCSI: | ||
288 | return oss->irq_pending & OSS_IP_SCSI; | ||
289 | break; | ||
290 | case IRQ_NUBUS_9: | ||
291 | case IRQ_NUBUS_A: | ||
292 | case IRQ_NUBUS_B: | ||
293 | case IRQ_NUBUS_C: | ||
294 | case IRQ_NUBUS_D: | ||
295 | case IRQ_NUBUS_E: | ||
296 | irq -= NUBUS_SOURCE_BASE; | ||
297 | return oss->irq_pending & (1 << irq); | ||
298 | break; | ||
299 | } | ||
300 | return 0; | ||
301 | } | ||