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 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 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 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 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 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 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 353 via1_noint(void *bitnum) 354 { 355 printf("via1_noint(%d)\n", (int)bitnum); 356 } 357 358 static void 359 via2_noint(void *bitnum) 360 { 361 printf("via2_noint(%d)\n", (int)bitnum); 362 } 363 364 int 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 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 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 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 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 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 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 (at) 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 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 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