xref: /netbsd/sys/arch/mac68k/mac68k/via.c (revision 0018643d)
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