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