1 /* $NetBSD: via.c,v 1.77 2024/02/28 13:05:40 thorpej 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.77 2024/02/28 13:05:40 thorpej 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 void
via1_intr(void * intr_arg)243 via1_intr(void *intr_arg /* struct clockframe * */)
244 {
245 u_int8_t intbits, bitnum;
246 u_int mask;
247
248 intbits = via_reg(VIA1, vIFR); /* get interrupts pending */
249 intbits &= via_reg(VIA1, vIER); /* only care about enabled */
250
251 if (intbits == 0)
252 return;
253
254 /*
255 * Unflag interrupts here. If we do it after each interrupt,
256 * the MRG ADB hangs up.
257 */
258 via_reg(VIA1, vIFR) = intbits;
259
260 intbits &= 0x7f;
261 mask = 1;
262 bitnum = 0;
263 do {
264 if (intbits & mask) {
265 /*
266 * We want to pass the clockframe on to
267 * rtclock_intr().
268 */
269 void *arg = via1itab[bitnum] == rtclock_intr
270 ? intr_arg : via1iarg[bitnum];
271 via1itab[bitnum](arg);
272 /* via_reg(VIA1, vIFR) = mask; */
273 }
274 mask <<= 1;
275 ++bitnum;
276 } while (intbits >= mask);
277 }
278
279 void
via2_intr(void * intr_arg)280 via2_intr(void *intr_arg)
281 {
282 u_int8_t intbits, bitnum;
283 u_int mask;
284
285 intbits = via2_reg(vIFR); /* get interrupts pending */
286 intbits &= via2_reg(vIER); /* only care about enabled */
287
288 if (intbits == 0)
289 return;
290
291 via2_reg(vIFR) = intbits;
292
293 intbits &= 0x7f;
294 mask = 1;
295 bitnum = 0;
296 do {
297 if (intbits & mask)
298 via2itab[bitnum](via2iarg[bitnum]);
299 mask <<= 1;
300 ++bitnum;
301 } while (intbits >= mask);
302 }
303
304 void
rbv_intr(void * intr_arg)305 rbv_intr(void *intr_arg)
306 {
307 u_int8_t intbits, bitnum;
308 u_int mask;
309
310 intbits = (via2_reg(vIFR + rIFR) & via2_reg(vIER + rIER));
311
312 if (intbits == 0)
313 return;
314
315 via2_reg(rIFR) = intbits;
316
317 intbits &= 0x7f;
318 mask = 1;
319 bitnum = 0;
320 do {
321 if (intbits & mask)
322 via2itab[bitnum](via2iarg[bitnum]);
323 mask <<= 1;
324 ++bitnum;
325 } while (intbits >= mask);
326 }
327
328 void
oss_intr(void * intr_arg)329 oss_intr(void *intr_arg)
330 {
331 u_int8_t intbits, bitnum;
332 u_int mask;
333
334 intbits = via2_reg(vIFR + rIFR);
335
336 if (intbits == 0)
337 return;
338
339 intbits &= 0x7f;
340 mask = 1;
341 bitnum = 0;
342 do {
343 if (intbits & mask) {
344 (*slotitab[bitnum])(slotptab[bitnum]);
345 via2_reg(rIFR) = mask;
346 }
347 mask <<= 1;
348 ++bitnum;
349 } while (intbits >= mask);
350 }
351
352 static void
via1_noint(void * bitnum)353 via1_noint(void *bitnum)
354 {
355 printf("via1_noint(%d)\n", (int)bitnum);
356 }
357
358 static void
via2_noint(void * bitnum)359 via2_noint(void *bitnum)
360 {
361 printf("via2_noint(%d)\n", (int)bitnum);
362 }
363
364 int
add_nubus_intr(int slot,void (* func)(void *),void * client_data)365 add_nubus_intr(int slot, void (*func)(void *), void *client_data)
366 {
367 int s;
368
369 /*
370 * Map Nubus slot 0 to "slot" 15; see note on Nubus slot
371 * interrupt tables.
372 */
373 if (slot == 0)
374 slot = 15;
375 if (slot < 9 || slot > 15)
376 return 0;
377
378 s = splhigh();
379
380 if (func == NULL) {
381 slotitab[slot - 9] = slot_noint;
382 nubus_intr_mask &= ~(1 << (slot - 9));
383 } else {
384 slotitab[slot - 9] = func;
385 nubus_intr_mask |= (1 << (slot - 9));
386 }
387 if (client_data == NULL)
388 slotptab[slot - 9] = (void *)(slot - 9);
389 else
390 slotptab[slot - 9] = client_data;
391
392 splx(s);
393
394 return 1;
395 }
396
397 void
enable_nubus_intr(void)398 enable_nubus_intr(void)
399 {
400 if ((nubus_intr_mask & 0x3f) == 0)
401 return;
402
403 if (VIA2 == VIA2OFF)
404 via2_reg(vIER) = 0x80 | V2IF_SLOTINT;
405 else
406 via2_reg(rIER) = 0x80 | V2IF_SLOTINT;
407 }
408
409 /*ARGSUSED*/
410 void
via2_nubus_intr(void * bitarg)411 via2_nubus_intr(void *bitarg)
412 {
413 u_int8_t i, intbits, mask;
414
415 via2_reg(vIFR) = V2IF_SLOTINT;
416 while ((intbits = (~via2_reg(vBufA)) & nubus_intr_mask)) {
417 i = 6;
418 mask = (1 << i);
419 do {
420 if (intbits & mask)
421 (*slotitab[i])(slotptab[i]);
422 i--;
423 mask >>= 1;
424 } while (mask);
425 via2_reg(vIFR) = V2IF_SLOTINT;
426 }
427 }
428
429 /*ARGSUSED*/
430 void
rbv_nubus_intr(void * bitarg)431 rbv_nubus_intr(void *bitarg)
432 {
433 u_int8_t i, intbits, mask;
434
435 via2_reg(rIFR) = 0x80 | V2IF_SLOTINT;
436 while ((intbits = (~via2_reg(rBufA)) & via2_reg(rSlotInt))) {
437 i = 6;
438 mask = (1 << i);
439 do {
440 if (intbits & mask)
441 (*slotitab[i])(slotptab[i]);
442 i--;
443 mask >>= 1;
444 } while (mask);
445 via2_reg(rIFR) = 0x80 | V2IF_SLOTINT;
446 }
447 }
448
449 static void
slot_ignore(void * client_data)450 slot_ignore(void *client_data)
451 {
452 int mask = (1 << (int)client_data);
453
454 if (VIA2 == VIA2OFF) {
455 via2_reg(vDirA) |= mask;
456 via2_reg(vBufA) = mask;
457 via2_reg(vDirA) &= ~mask;
458 } else
459 via2_reg(rBufA) = mask;
460 }
461
462 static void
slot_noint(void * client_data)463 slot_noint(void *client_data)
464 {
465 int slot = (int)client_data + 9;
466
467 printf("slot_noint() slot %x\n", slot);
468
469 /* attempt to clear the interrupt */
470 slot_ignore(client_data);
471 }
472
473 void
via_powerdown(void)474 via_powerdown(void)
475 {
476 if (VIA2 == VIA2OFF) {
477 via2_reg(vDirB) |= 0x04; /* Set write for bit 2 */
478 via2_reg(vBufB) &= ~0x04; /* Shut down */
479 } else if (VIA2 == RBVOFF) {
480 via2_reg(rBufB) &= ~0x04;
481 } else if (VIA2 == OSSOFF) {
482 /*
483 * Thanks to Brad Boyer <flar@cegt201.bradley.edu> for the
484 * Linux/mac68k code that I derived this from.
485 */
486 via2_reg(OSS_oRCR) |= OSS_POWEROFF;
487 }
488 }
489
490 void
via1_register_irq(int irq,void (* irq_func)(void *),void * client_data)491 via1_register_irq(int irq, void (*irq_func)(void *), void *client_data)
492 {
493 if (irq_func) {
494 via1itab[irq] = irq_func;
495 via1iarg[irq] = client_data;
496 } else {
497 via1itab[irq] = via1_noint;
498 via1iarg[irq] = (void *)0;
499 }
500 }
501
502 void
via2_register_irq(int irq,void (* irq_func)(void *),void * client_data)503 via2_register_irq(int irq, void (*irq_func)(void *), void *client_data)
504 {
505 if (irq_func) {
506 via2itab[irq] = irq_func;
507 via2iarg[irq] = client_data;
508 } else {
509 via2itab[irq] = via2_noint;
510 via2iarg[irq] = (void *)0;
511 }
512 }
513