1 /* $NetBSD: via.c,v 1.76 2020/07/21 06:10:26 rin Exp $ */
2
3 /*-
4 * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo,
5 * Michael L. Finch, Bradley A. Grantham, and
6 * Lawrence A. Kesteloot
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the Alice Group.
20 * 4. The names of the Alice Group or any of its members may not be used
21 * to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 /*
38 * This code handles VIA, RBV, and OSS functionality.
39 */
40
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: via.c,v 1.76 2020/07/21 06:10:26 rin Exp $");
43
44 #include "opt_mac68k.h"
45
46 #include <sys/param.h>
47 #include <sys/kernel.h>
48 #include <sys/syslog.h>
49 #include <sys/systm.h>
50 #include <machine/cpu.h>
51 #include <machine/frame.h>
52 #include <machine/intr.h>
53 #include <machine/viareg.h>
54
55 void mrg_adbintr(void *);
56 void mrg_pmintr(void *);
57 void rtclock_intr(void *);
58 void profclock(void *);
59
60 void via1_intr(void *);
61 void via2_intr(void *);
62 void rbv_intr(void *);
63 void oss_intr(void *);
64 void via2_nubus_intr(void *);
65 void rbv_nubus_intr(void *);
66
67 static void via1_noint(void *);
68 static void via2_noint(void *);
69 static void slot_ignore(void *);
70 static void slot_noint(void *);
71
72 int VIA2 = VIA2OFF; /* default for II, IIx, IIcx, SE/30. */
73
74 /* VIA1 interrupt handler table */
75 void (*via1itab[7])(void *) = {
76 via1_noint,
77 via1_noint,
78 mrg_adbintr,
79 via1_noint,
80 mrg_pmintr,
81 via1_noint,
82 rtclock_intr,
83 };
84
85 /* Arg array for VIA1 interrupts. */
86 void *via1iarg[7] = {
87 (void *)0,
88 (void *)1,
89 (void *)2,
90 (void *)3,
91 (void *)4,
92 (void *)5,
93 (void *)6
94 };
95
96 /* VIA2 interrupt handler table */
97 void (*via2itab[7])(void *) = {
98 via2_noint,
99 via2_nubus_intr,
100 via2_noint,
101 via2_noint,
102 via2_noint, /* snd_intr */
103 via2_noint, /* via2t2_intr */
104 via2_noint,
105 };
106
107 /* Arg array for VIA2 interrupts. */
108 void *via2iarg[7] = {
109 (void *)0,
110 (void *)1,
111 (void *)2,
112 (void *)3,
113 (void *)4,
114 (void *)5,
115 (void *)6
116 };
117
118 /*
119 * Nubus slot interrupt routines and parameters for slots 9-15. Note
120 * that for simplicity of code, "v2IRQ0" for internal video is treated
121 * as a slot 15 interrupt; this slot is quite fictitious in real-world
122 * Macs. See also GMFH, pp. 165-167, and "Monster, Loch Ness."
123 */
124 void (*slotitab[7])(void *) = {
125 slot_noint,
126 slot_noint,
127 slot_noint,
128 slot_noint,
129 slot_noint,
130 slot_noint,
131 slot_noint /* int_video_intr */
132 };
133
134 void *slotptab[7] = {
135 (void *)0,
136 (void *)1,
137 (void *)2,
138 (void *)3,
139 (void *)4,
140 (void *)5,
141 (void *)6
142 };
143
144 static int nubus_intr_mask = 0;
145
146 void
via_init(void)147 via_init(void)
148 {
149 /* Initialize VIA1 */
150 /* set all timers to 0 */
151 via_reg(VIA1, vT1L) = 0;
152 via_reg(VIA1, vT1LH) = 0;
153 via_reg(VIA1, vT1C) = 0;
154 via_reg(VIA1, vT1CH) = 0;
155 via_reg(VIA1, vT2C) = 0;
156 via_reg(VIA1, vT2CH) = 0;
157
158 /* turn off timer latch */
159 via_reg(VIA1, vACR) &= 0x3f;
160
161 intr_establish((int (*)(void *)) via1_intr, NULL, mac68k_machine.via1_ipl);
162
163 if (VIA2 == VIA2OFF) {
164 /* Initialize VIA2 */
165 via2_reg(vT1L) = 0;
166 via2_reg(vT1LH) = 0;
167 via2_reg(vT1C) = 0;
168 via2_reg(vT1CH) = 0;
169 via2_reg(vT2C) = 0;
170 via2_reg(vT2CH) = 0;
171
172 /* turn off timer latch */
173 via2_reg(vACR) &= 0x3f;
174
175 /*
176 * Turn off SE/30 video interrupts.
177 */
178 if (mac68k_machine.machineid == MACH_MACSE30) {
179 via_reg(VIA1, vBufB) |= (0x40);
180 via_reg(VIA1, vDirB) |= (0x40);
181 }
182
183 /*
184 * Set vPCR for SCSI interrupts.
185 */
186 via2_reg(vPCR) = 0x66;
187 switch(mac68k_machine.machineid) {
188 case MACH_MACPB140:
189 case MACH_MACPB145:
190 case MACH_MACPB150:
191 case MACH_MACPB160:
192 case MACH_MACPB165:
193 case MACH_MACPB165C:
194 case MACH_MACPB170:
195 case MACH_MACPB180:
196 case MACH_MACPB180C:
197 break;
198 default:
199 via2_reg(vBufB) |= 0x02; /* Unlock NuBus */
200 via2_reg(vDirB) |= 0x02;
201 break;
202 }
203
204 intr_establish((int (*)(void*))via2_intr, NULL,
205 mac68k_machine.via2_ipl);
206 via2itab[1] = via2_nubus_intr;
207 } else if (current_mac_model->class == MACH_CLASSIIfx) { /* OSS */
208 volatile u_char *ossintr;
209 ossintr = (volatile u_char *)IOBase + 0x1a006;
210 *ossintr = 0;
211 intr_establish((int (*)(void*))oss_intr, NULL,
212 mac68k_machine.via2_ipl);
213 } else { /* RBV */
214 #ifdef DISABLE_EXT_CACHE
215 if (current_mac_model->class == MACH_CLASSIIci) {
216 /*
217 * Disable cache card. (p. 174 -- GMFH)
218 */
219 via2_reg(rBufB) |= DB2O_CEnable;
220 }
221 #endif
222 intr_establish((int (*)(void*))rbv_intr, NULL,
223 mac68k_machine.via2_ipl);
224 via2itab[1] = rbv_nubus_intr;
225 add_nubus_intr(0, slot_ignore, NULL);
226 }
227 }
228
229 /*
230 * Set the state of the modem serial port's clock source.
231 */
232 void
via_set_modem(int onoff)233 via_set_modem(int onoff)
234 {
235 via_reg(VIA1, vDirA) |= DA1O_vSync;
236 if (onoff)
237 via_reg(VIA1, vBufA) |= DA1O_vSync;
238 else
239 via_reg(VIA1, vBufA) &= ~DA1O_vSync;
240 }
241
242 #if __GNUC_PREREQ__(8, 0)
243 /*
244 * XXX rtclock_intr() requires this for unwinding stack frame.
245 */
246 #pragma GCC push_options
247 #pragma GCC optimize "-fno-omit-frame-pointer"
248 #endif
249 void
via1_intr(void * intr_arg)250 via1_intr(void *intr_arg)
251 {
252 u_int8_t intbits, bitnum;
253 u_int mask;
254
255 intbits = via_reg(VIA1, vIFR); /* get interrupts pending */
256 intbits &= via_reg(VIA1, vIER); /* only care about enabled */
257
258 if (intbits == 0)
259 return;
260
261 /*
262 * Unflag interrupts here. If we do it after each interrupt,
263 * the MRG ADB hangs up.
264 */
265 via_reg(VIA1, vIFR) = intbits;
266
267 intbits &= 0x7f;
268 mask = 1;
269 bitnum = 0;
270 do {
271 if (intbits & mask) {
272 via1itab[bitnum](via1iarg[bitnum]);
273 /* via_reg(VIA1, vIFR) = mask; */
274 }
275 mask <<= 1;
276 ++bitnum;
277 } while (intbits >= mask);
278 }
279 #if __GNUC_PREREQ__(8, 0)
280 #pragma GCC pop_options
281 #endif
282
283 void
via2_intr(void * intr_arg)284 via2_intr(void *intr_arg)
285 {
286 u_int8_t intbits, bitnum;
287 u_int mask;
288
289 intbits = via2_reg(vIFR); /* get interrupts pending */
290 intbits &= via2_reg(vIER); /* only care about enabled */
291
292 if (intbits == 0)
293 return;
294
295 via2_reg(vIFR) = intbits;
296
297 intbits &= 0x7f;
298 mask = 1;
299 bitnum = 0;
300 do {
301 if (intbits & mask)
302 via2itab[bitnum](via2iarg[bitnum]);
303 mask <<= 1;
304 ++bitnum;
305 } while (intbits >= mask);
306 }
307
308 void
rbv_intr(void * intr_arg)309 rbv_intr(void *intr_arg)
310 {
311 u_int8_t intbits, bitnum;
312 u_int mask;
313
314 intbits = (via2_reg(vIFR + rIFR) & via2_reg(vIER + rIER));
315
316 if (intbits == 0)
317 return;
318
319 via2_reg(rIFR) = intbits;
320
321 intbits &= 0x7f;
322 mask = 1;
323 bitnum = 0;
324 do {
325 if (intbits & mask)
326 via2itab[bitnum](via2iarg[bitnum]);
327 mask <<= 1;
328 ++bitnum;
329 } while (intbits >= mask);
330 }
331
332 void
oss_intr(void * intr_arg)333 oss_intr(void *intr_arg)
334 {
335 u_int8_t intbits, bitnum;
336 u_int mask;
337
338 intbits = via2_reg(vIFR + rIFR);
339
340 if (intbits == 0)
341 return;
342
343 intbits &= 0x7f;
344 mask = 1;
345 bitnum = 0;
346 do {
347 if (intbits & mask) {
348 (*slotitab[bitnum])(slotptab[bitnum]);
349 via2_reg(rIFR) = mask;
350 }
351 mask <<= 1;
352 ++bitnum;
353 } while (intbits >= mask);
354 }
355
356 static void
via1_noint(void * bitnum)357 via1_noint(void *bitnum)
358 {
359 printf("via1_noint(%d)\n", (int)bitnum);
360 }
361
362 static void
via2_noint(void * bitnum)363 via2_noint(void *bitnum)
364 {
365 printf("via2_noint(%d)\n", (int)bitnum);
366 }
367
368 int
add_nubus_intr(int slot,void (* func)(void *),void * client_data)369 add_nubus_intr(int slot, void (*func)(void *), void *client_data)
370 {
371 int s;
372
373 /*
374 * Map Nubus slot 0 to "slot" 15; see note on Nubus slot
375 * interrupt tables.
376 */
377 if (slot == 0)
378 slot = 15;
379 if (slot < 9 || slot > 15)
380 return 0;
381
382 s = splhigh();
383
384 if (func == NULL) {
385 slotitab[slot - 9] = slot_noint;
386 nubus_intr_mask &= ~(1 << (slot - 9));
387 } else {
388 slotitab[slot - 9] = func;
389 nubus_intr_mask |= (1 << (slot - 9));
390 }
391 if (client_data == NULL)
392 slotptab[slot - 9] = (void *)(slot - 9);
393 else
394 slotptab[slot - 9] = client_data;
395
396 splx(s);
397
398 return 1;
399 }
400
401 void
enable_nubus_intr(void)402 enable_nubus_intr(void)
403 {
404 if ((nubus_intr_mask & 0x3f) == 0)
405 return;
406
407 if (VIA2 == VIA2OFF)
408 via2_reg(vIER) = 0x80 | V2IF_SLOTINT;
409 else
410 via2_reg(rIER) = 0x80 | V2IF_SLOTINT;
411 }
412
413 /*ARGSUSED*/
414 void
via2_nubus_intr(void * bitarg)415 via2_nubus_intr(void *bitarg)
416 {
417 u_int8_t i, intbits, mask;
418
419 via2_reg(vIFR) = V2IF_SLOTINT;
420 while ((intbits = (~via2_reg(vBufA)) & nubus_intr_mask)) {
421 i = 6;
422 mask = (1 << i);
423 do {
424 if (intbits & mask)
425 (*slotitab[i])(slotptab[i]);
426 i--;
427 mask >>= 1;
428 } while (mask);
429 via2_reg(vIFR) = V2IF_SLOTINT;
430 }
431 }
432
433 /*ARGSUSED*/
434 void
rbv_nubus_intr(void * bitarg)435 rbv_nubus_intr(void *bitarg)
436 {
437 u_int8_t i, intbits, mask;
438
439 via2_reg(rIFR) = 0x80 | V2IF_SLOTINT;
440 while ((intbits = (~via2_reg(rBufA)) & via2_reg(rSlotInt))) {
441 i = 6;
442 mask = (1 << i);
443 do {
444 if (intbits & mask)
445 (*slotitab[i])(slotptab[i]);
446 i--;
447 mask >>= 1;
448 } while (mask);
449 via2_reg(rIFR) = 0x80 | V2IF_SLOTINT;
450 }
451 }
452
453 static void
slot_ignore(void * client_data)454 slot_ignore(void *client_data)
455 {
456 int mask = (1 << (int)client_data);
457
458 if (VIA2 == VIA2OFF) {
459 via2_reg(vDirA) |= mask;
460 via2_reg(vBufA) = mask;
461 via2_reg(vDirA) &= ~mask;
462 } else
463 via2_reg(rBufA) = mask;
464 }
465
466 static void
slot_noint(void * client_data)467 slot_noint(void *client_data)
468 {
469 int slot = (int)client_data + 9;
470
471 printf("slot_noint() slot %x\n", slot);
472
473 /* attempt to clear the interrupt */
474 slot_ignore(client_data);
475 }
476
477 void
via_powerdown(void)478 via_powerdown(void)
479 {
480 if (VIA2 == VIA2OFF) {
481 via2_reg(vDirB) |= 0x04; /* Set write for bit 2 */
482 via2_reg(vBufB) &= ~0x04; /* Shut down */
483 } else if (VIA2 == RBVOFF) {
484 via2_reg(rBufB) &= ~0x04;
485 } else if (VIA2 == OSSOFF) {
486 /*
487 * Thanks to Brad Boyer <flar@cegt201.bradley.edu> for the
488 * Linux/mac68k code that I derived this from.
489 */
490 via2_reg(OSS_oRCR) |= OSS_POWEROFF;
491 }
492 }
493
494 void
via1_register_irq(int irq,void (* irq_func)(void *),void * client_data)495 via1_register_irq(int irq, void (*irq_func)(void *), void *client_data)
496 {
497 if (irq_func) {
498 via1itab[irq] = irq_func;
499 via1iarg[irq] = client_data;
500 } else {
501 via1itab[irq] = via1_noint;
502 via1iarg[irq] = (void *)0;
503 }
504 }
505
506 void
via2_register_irq(int irq,void (* irq_func)(void *),void * client_data)507 via2_register_irq(int irq, void (*irq_func)(void *), void *client_data)
508 {
509 if (irq_func) {
510 via2itab[irq] = irq_func;
511 via2iarg[irq] = client_data;
512 } else {
513 via2itab[irq] = via2_noint;
514 via2iarg[irq] = (void *)0;
515 }
516 }
517