1 /* $NetBSD: machdep.c,v 1.55 2024/09/08 10:17:54 andvar Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.55 2024/09/08 10:17:54 andvar Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/device.h> 39 #include <sys/mount.h> 40 #include <sys/msgbuf.h> 41 #include <sys/kernel.h> 42 #include <sys/reboot.h> 43 #include <sys/ksyms.h> 44 45 #include <uvm/uvm_extern.h> 46 47 #include <dev/cons.h> 48 49 #include <machine/autoconf.h> 50 #include <machine/powerpc.h> 51 52 #include <powerpc/oea/bat.h> 53 #include <powerpc/pic/picvar.h> 54 55 #include <amiga/amiga/cc.h> 56 #include <amiga/amiga/cia.h> 57 #include <amiga/amiga/custom.h> 58 #include <amiga/amiga/device.h> 59 #include <amiga/amiga/isr.h> 60 #include <amiga/amiga/memlist.h> 61 #include <amigappc/amigappc/p5reg.h> 62 63 #include "opt_ddb.h" 64 65 #include "fd.h" 66 #include "ser.h" 67 68 extern void setup_amiga_intr(void); 69 #if NSER > 0 70 extern void ser_outintr(void); 71 extern void ser_fastint(void); 72 #endif 73 #if NFD > 0 74 extern void fdintr(int); 75 #endif 76 77 #define AMIGAMEMREGIONS 8 78 static struct mem_region physmemr[AMIGAMEMREGIONS], availmemr[AMIGAMEMREGIONS]; 79 static char model[80]; 80 81 /* 82 * patched by some devices at attach time (currently, only the coms) 83 */ 84 int amiga_serialspl = 4; 85 86 /* 87 * current open serial device speed; used by some SCSI drivers to reduce 88 * DMA transfer lengths. 89 */ 90 int ser_open_speed; 91 92 /* interrupt handler chains for level2 and level6 interrupt */ 93 struct isr *isr_ports; 94 struct isr *isr_exter; 95 96 extern void *startsym, *endsym; 97 98 void 99 add_isr(struct isr *isr) 100 { 101 struct isr **p, *q; 102 103 p = isr->isr_ipl == 2 ? &isr_ports : &isr_exter; 104 105 while ((q = *p) != NULL) 106 p = &q->isr_forw; 107 isr->isr_forw = NULL; 108 *p = isr; 109 110 /* enable interrupt */ 111 custom.intena = isr->isr_ipl == 2 ? 112 INTF_SETCLR | INTF_PORTS : 113 INTF_SETCLR | INTF_EXTER; 114 } 115 116 void 117 remove_isr(struct isr *isr) 118 { 119 struct isr **p, *q; 120 121 p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports; 122 123 while ((q = *p) != NULL && q != isr) 124 p = &q->isr_forw; 125 if (q) 126 *p = q->isr_forw; 127 else 128 panic("remove_isr: handler not registered"); 129 130 /* disable interrupt if no more handlers */ 131 p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports; 132 if (*p == NULL) { 133 custom.intena = isr->isr_ipl == 6 ? 134 INTF_EXTER : INTF_PORTS; 135 } 136 } 137 138 static int 139 ports_intr(void *arg) 140 { 141 struct isr **p; 142 struct isr *q; 143 144 p = (struct isr **)arg; 145 while ((q = *p) != NULL) { 146 if ((q->isr_intr)(q->isr_arg)) 147 break; 148 p = &q->isr_forw; 149 } 150 if (q == NULL) 151 ciaa_intr(); /* ciaa handles keyboard and parallel port */ 152 153 custom.intreq = INTF_PORTS; 154 return 0; 155 } 156 157 static int 158 exter_intr(void *arg) 159 { 160 struct isr **p; 161 struct isr *q; 162 163 p = (struct isr **)arg; 164 while ((q = *p) != NULL) { 165 if ((q->isr_intr)(q->isr_arg)) 166 break; 167 p = &q->isr_forw; 168 } 169 if (q == NULL) 170 ciab_intr(); /* clear ciab icr */ 171 172 custom.intreq = INTF_EXTER; 173 return 0; 174 } 175 176 static int 177 lev1_intr(void *arg) 178 { 179 unsigned short ireq; 180 181 ireq = custom.intreqr; 182 if (ireq & INTF_TBE) { 183 #if NSER > 0 184 ser_outintr(); 185 #else 186 custom.intreq = INTF_TBE; 187 #endif 188 } 189 if (ireq & INTF_DSKBLK) { 190 #if NFD > 0 191 fdintr(0); 192 #endif 193 custom.intreq = INTF_DSKBLK; 194 } 195 if (ireq & INTF_SOFTINT) { 196 #ifdef DEBUG 197 printf("intrhand: SOFTINT ignored\n"); 198 #endif 199 custom.intreq = INTF_SOFTINT; 200 } 201 return 0; 202 } 203 204 static int 205 lev3_intr(void *arg) 206 { 207 unsigned short ireq; 208 209 ireq = custom.intreqr; 210 if (ireq & INTF_BLIT) 211 blitter_handler(); 212 if (ireq & INTF_COPER) 213 copper_handler(); 214 if (ireq & INTF_VERTB) 215 vbl_handler(); 216 return 0; 217 } 218 219 static int 220 lev4_intr(void *arg) 221 { 222 223 audio_handler(); 224 return 0; 225 } 226 227 static int 228 lev5_intr(void *arg) 229 { 230 231 ser_fastint(); 232 if (custom.intreqr & INTF_DSKSYNC) 233 custom.intreq = INTF_DSKSYNC; 234 return 0; 235 } 236 237 static void 238 amigappc_install_handlers(void) 239 { 240 241 /* handlers for all 6 Amiga interrupt levels */ 242 intr_establish(1, IST_LEVEL, IPL_BIO, lev1_intr, NULL); 243 intr_establish(2, IST_LEVEL, IPL_BIO, ports_intr, &isr_ports); 244 intr_establish(3, IST_LEVEL, IPL_TTY, lev3_intr, NULL); 245 intr_establish(4, IST_LEVEL, IPL_AUDIO, lev4_intr, NULL); 246 intr_establish(5, IST_LEVEL, IPL_SERIAL, lev5_intr, NULL); 247 intr_establish(6, IST_LEVEL, IPL_SERIAL, exter_intr, &isr_exter); 248 } 249 250 static void 251 amigappc_identify(void) 252 { 253 extern u_long ns_per_tick, ticks_per_sec; 254 static const unsigned char pll[] = { 255 10, 10, 70, 10, 20, 65, 25, 45, 256 30, 55, 40, 50, 15, 60, 35, 00 257 }; 258 const char *cpuname, *mach, *p5type_p, *pup; 259 u_long busclock, cpuclock; 260 register int pvr, hid1; 261 262 /* PowerUp ROM id location */ 263 p5type_p = (const char *)0xf00010; 264 265 /* 266 * PVR holds the CPU-type and version while 267 * HID1 holds the PLL configuration 268 */ 269 __asm ("mfpvr %0; mfspr %1,1009" : "=r"(pvr), "=r"(hid1)); 270 271 /* Amiga types which can run a PPC board */ 272 if (is_a4000()) 273 mach = "Amiga 4000"; 274 else if (is_a3000()) 275 mach = "Amiga 3000"; 276 else 277 mach = "Amiga 1200"; 278 279 /* find CPU type - BlizzardPPC has 603e/603ev, CyberstormPPC has 604e */ 280 switch (pvr >> 16) { 281 case 3: 282 cpuname = "603"; 283 break; 284 case 4: 285 cpuname = "604"; 286 break; 287 case 6: 288 cpuname = "603e"; 289 break; 290 case 7: 291 cpuname = "603ev"; 292 break; 293 case 9: 294 case 10: 295 cpuname = "604e"; 296 break; 297 default: 298 cpuname = "unknown"; 299 break; 300 } 301 302 switch (p5type_p[0]) { 303 case 'D': 304 pup = "[PowerUP]"; 305 break; 306 case 'E': 307 pup = "[CSPPC]"; 308 break; 309 case 'F': 310 pup = "[CS Mk.III]"; 311 break; 312 case 'H': 313 case 'I': 314 pup = "[BlizzardPPC]"; 315 break; 316 default: 317 pup = ""; 318 break; 319 } 320 321 /* XXX busclock can be measured with CIA-timers */ 322 switch (p5type_p[1]) { 323 case 'A': 324 busclock = 60000000; 325 break; 326 /* case B, C, D */ 327 default: 328 busclock = 66666667; 329 break; 330 } 331 332 /* compute cpuclock based on PLL configuration */ 333 cpuclock = busclock * pll[hid1>>28 & 0xf] / 10; 334 335 snprintf(model, sizeof(model), 336 "%s %s (%s v%d.%d %lu MHz, busclk %lu MHz)", 337 mach, pup, cpuname, (pvr>>8) & 0xf, (pvr >> 0) & 0xf, 338 cpuclock / 1000000, busclock / 1000000); 339 340 /* set timebase */ 341 ticks_per_sec = busclock / 4; 342 ns_per_tick = 1000000000 / ticks_per_sec; 343 } 344 345 static void 346 amigappc_reboot(void) 347 { 348 349 /* reboot CSPPC/BPPC */ 350 if (!is_a1200()) { 351 P5write(P5_REG_LOCK,0x60); 352 P5write(P5_REG_LOCK,0x50); 353 P5write(P5_REG_LOCK,0x30); 354 P5write(P5_REG_SHADOW,P5_SET_CLEAR|P5_SHADOW); 355 P5write(P5_REG_LOCK,0x00); 356 } else 357 P5write(P5_BPPC_MAGIC,0x00); 358 359 P5write(P5_REG_LOCK,0x60); 360 P5write(P5_REG_LOCK,0x50); 361 P5write(P5_REG_LOCK,0x30); 362 P5write(P5_REG_SHADOW,P5_SELF_RESET); 363 P5write(P5_REG_RESET,P5_AMIGA_RESET); 364 } 365 366 static void 367 amigappc_bat_add(paddr_t pa, register_t len, register_t prot) 368 { 369 static int nd = 0, ni = 0; 370 const uint32_t i = pa >> 28; 371 372 battable[i].batl = BATL(pa, prot, BAT_PP_RW); 373 battable[i].batu = BATU(pa, len, BAT_Vs); 374 375 /* 376 * Let's start loading the BAT registers. 377 */ 378 if (!(prot & (BAT_I | BAT_G))) { 379 switch (ni) { 380 case 0: 381 __asm volatile ("isync"); 382 __asm volatile ("mtibatl 0,%0; mtibatu 0,%1;" 383 :: "r"(battable[i].batl), 384 "r"(battable[i].batu)); 385 __asm volatile ("isync"); 386 ni = 1; 387 break; 388 case 1: 389 __asm volatile ("isync"); 390 __asm volatile ("mtibatl 1,%0; mtibatu 1,%1;" 391 :: "r"(battable[i].batl), 392 "r"(battable[i].batu)); 393 __asm volatile ("isync"); 394 ni = 2; 395 break; 396 case 2: 397 __asm volatile ("isync"); 398 __asm volatile ("mtibatl 2,%0; mtibatu 2,%1;" 399 :: "r"(battable[i].batl), 400 "r"(battable[i].batu)); 401 __asm volatile ("isync"); 402 ni = 3; 403 break; 404 case 3: 405 __asm volatile ("isync"); 406 __asm volatile ("mtibatl 3,%0; mtibatu 3,%1;" 407 :: "r"(battable[i].batl), 408 "r"(battable[i].batu)); 409 __asm volatile ("isync"); 410 ni = 4; 411 break; 412 default: 413 break; 414 } 415 } 416 switch (nd) { 417 case 0: 418 __asm volatile ("isync"); 419 __asm volatile ("mtdbatl 0,%0; mtdbatu 0,%1;" 420 :: "r"(battable[i].batl), 421 "r"(battable[i].batu)); 422 __asm volatile ("isync"); 423 nd = 1; 424 break; 425 case 1: 426 __asm volatile ("isync"); 427 __asm volatile ("mtdbatl 1,%0; mtdbatu 1,%1;" 428 :: "r"(battable[i].batl), 429 "r"(battable[i].batu)); 430 __asm volatile ("isync"); 431 nd = 2; 432 break; 433 case 2: 434 __asm volatile ("isync"); 435 __asm volatile ("mtdbatl 2,%0; mtdbatu 2,%1;" 436 :: "r"(battable[i].batl), 437 "r"(battable[i].batu)); 438 __asm volatile ("isync"); 439 nd = 3; 440 break; 441 case 3: 442 __asm volatile ("isync"); 443 __asm volatile ("mtdbatl 3,%0; mtdbatu 3,%1;" 444 :: "r"(battable[i].batl), 445 "r"(battable[i].batu)); 446 __asm volatile ("isync"); 447 nd = 4; 448 break; 449 default: 450 break; 451 } 452 } 453 454 static void 455 amigappc_batinit(paddr_t pa, ...) 456 { 457 va_list ap; 458 register_t msr; 459 460 msr = mfmsr(); 461 462 /* 463 * Set BAT registers to unmapped to avoid overlapping mappings below. 464 */ 465 if ((msr & (PSL_IR|PSL_DR)) == 0) { 466 __asm volatile ("isync"); 467 __asm volatile ("mtibatu 0,%0" :: "r"(0)); 468 __asm volatile ("mtibatu 1,%0" :: "r"(0)); 469 __asm volatile ("mtibatu 2,%0" :: "r"(0)); 470 __asm volatile ("mtibatu 3,%0" :: "r"(0)); 471 __asm volatile ("mtdbatu 0,%0" :: "r"(0)); 472 __asm volatile ("mtdbatu 1,%0" :: "r"(0)); 473 __asm volatile ("mtdbatu 2,%0" :: "r"(0)); 474 __asm volatile ("mtdbatu 3,%0" :: "r"(0)); 475 __asm volatile ("isync"); 476 } 477 478 /* 479 * Setup BATs 480 */ 481 va_start(ap, pa); 482 while (pa != ~0) { 483 register_t len, prot; 484 485 len = va_arg(ap, register_t); 486 prot = va_arg(ap, register_t); 487 amigappc_bat_add(pa, len, prot); 488 pa = va_arg(ap, paddr_t); 489 } 490 va_end(ap); 491 } 492 493 void 494 initppc(u_int startkernel, u_int endkernel) 495 { 496 struct boot_memseg *ms; 497 u_int i, r; 498 499 /* 500 * amigappc memory region set 501 */ 502 ms = &memlist->m_seg[0]; 503 for (i = 0, r = 0; i < memlist->m_nseg; i++, ms++) { 504 if (ms->ms_attrib & MEMF_FAST) { 505 /* 506 * XXX Only recognize the memory segment in which 507 * the kernel resides, for now 508 */ 509 if (ms->ms_start <= startkernel && 510 ms->ms_start + ms->ms_size > endkernel) { 511 physmemr[r].start = (paddr_t)ms->ms_start; 512 physmemr[r].size = (psize_t)ms->ms_size & ~PGOFSET; 513 availmemr[r].start = (endkernel + PGOFSET) & ~PGOFSET; 514 availmemr[r].size = (physmemr[0].start + 515 physmemr[0].size) 516 - availmemr[0].start; 517 } 518 r++; 519 } 520 } 521 physmemr[r].start = 0; 522 physmemr[r].size = 0; 523 availmemr[r].start = 0; 524 availmemr[r].size = 0; 525 if (r == 0) 526 panic("initppc: no suitable memory segment found"); 527 528 /* 529 * Initialize BAT tables. 530 * The CSPPC RAM (A3000/A4000) always starts at 0x08000000 and is 531 * up to 128MB big. 532 * The BPPC RAM (A1200) can be up to 256MB and may start at nearly 533 * any address between 0x40000000 and 0x80000000 depending on which 534 * RAM module of which size was inserted into which bank: 535 * The RAM module in bank 1 is located from 0x?8000000 downwards. 536 * The RAM module in bank 2 is located from 0x?8000000 upwards. 537 * Whether '?' is 4, 5, 6 or 7 probably depends on the size. 538 * So we have to use the 'startkernel' symbol for BAT-mapping 539 * our RAM. 540 */ 541 if (is_a1200()) { 542 amigappc_batinit(0x00000000, BAT_BL_16M, BAT_I|BAT_G, 543 (startkernel & 0xf0000000), BAT_BL_256M, 0, 544 0xfff00000, BAT_BL_512K, 0, 545 ~0); 546 } else { 547 /* A3000 or A4000 */ 548 amigappc_batinit(0x00000000, BAT_BL_16M, BAT_I|BAT_G, 549 (startkernel & 0xf8000000), BAT_BL_128M, 0, 550 0xfff00000, BAT_BL_512K, 0, 551 0x40000000, BAT_BL_256M, BAT_I|BAT_G, 552 ~0); 553 } 554 555 /* 556 * Set up trap vectors and interrupt handler 557 */ 558 oea_init(NULL); 559 560 /* XXX bus_space_init() not needed here */ 561 562 uvm_md_init(); 563 564 /* 565 * Initialize pmap module 566 */ 567 pmap_bootstrap(startkernel, endkernel); 568 569 #if NKSYMS || defined(DDB) || defined(MODULAR) 570 ksyms_addsyms_elf((int)((u_int)endsym - (u_int)startsym), startsym, endsym); 571 #endif 572 573 /* 574 * CPU model, bus clock, timebase 575 */ 576 amigappc_identify(); 577 578 #ifdef DDB 579 if (boothowto & RB_KDB) 580 Debugger(); 581 #endif 582 } 583 584 /* 585 * This is called during initppc, before the system is really initialized. 586 * It shall provide the total and the available regions of RAM. 587 * Both lists must have a zero-size entry as terminator. 588 * The available regions need not take the kernel into account, but needs 589 * to provide space for two additional entry beyond the terminating one. 590 */ 591 void 592 mem_regions(struct mem_region **memp, struct mem_region **availp) 593 { 594 595 *memp = physmemr; 596 *availp = availmemr; 597 } 598 599 /* 600 * Machine dependent startup code 601 */ 602 void 603 cpu_startup(void) 604 { 605 int msr; 606 607 /* 608 * hello world 609 */ 610 oea_startup(model); 611 612 /* Setup interrupts */ 613 pic_init(); 614 setup_amiga_intr(); 615 amigappc_install_handlers(); 616 617 oea_install_extint(pic_ext_intr); 618 619 /* 620 * Now allow hardware interrupts. 621 */ 622 splhigh(); 623 __asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0" 624 : "=r"(msr) 625 : "K"(PSL_EE)); 626 627 #if 0 /* XXX Not in amiga bus.h - fix it? */ 628 /* 629 * Now that we have VM, malloc's are OK in bus_space. 630 */ 631 bus_space_mallocok(); 632 #endif 633 } 634 635 /* 636 * consinit 637 * Initialize system console. 638 */ 639 void 640 consinit(void) 641 { 642 643 /* preconfigure graphics cards */ 644 custom_chips_init(); 645 config_console(); 646 647 /* Initialize the console before we print anything out. */ 648 cninit(); 649 } 650 651 /* 652 * Halt or reboot the machine after syncing/dumping according to howto 653 */ 654 void 655 cpu_reboot(int howto, char *what) 656 { 657 static int syncing; 658 659 if (cold) { 660 howto |= RB_HALT; 661 goto halt_sys; 662 } 663 664 boothowto = howto; 665 if ((howto & RB_NOSYNC) == 0 && syncing == 0) { 666 syncing = 1; 667 vfs_shutdown(); /* sync */ 668 } 669 670 /* Disable intr */ 671 splhigh(); 672 673 /* Do dump if requested */ 674 if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) { 675 oea_dumpsys(); 676 #ifdef DDB 677 /* XXX dumpsys doesn't work, so give a chance to debug */ 678 Debugger(); 679 #endif 680 } 681 682 halt_sys: 683 doshutdownhooks(); 684 685 if (howto & RB_HALT) { 686 printf("\n"); 687 printf("The operating system has halted.\n"); 688 printf("Please press any key to reboot.\n\n"); 689 cnpollc(1); /* for proper keyboard command handling */ 690 cngetc(); 691 cnpollc(0); 692 } 693 694 printf("rebooting...\n\n"); 695 delay(1000000); 696 amigappc_reboot(); 697 698 for (;;); 699 /* NOTREACHED */ 700 } 701 702 /* 703 * Try to emulate the functionality from m68k/m68k/sys_machdep.c 704 * used by several amiga scsi drivers. 705 */ 706 int 707 dma_cachectl(void *addr, int len) 708 { 709 paddr_t pa, end; 710 int inc; 711 712 if (addr == NULL || len == 0) 713 return 0; 714 715 pa = kvtop(addr); 716 inc = curcpu()->ci_ci.dcache_line_size; 717 718 for (end = pa + len; pa < end; pa += inc) 719 __asm volatile("dcbf 0,%0" :: "r"(pa)); 720 __asm volatile("sync"); 721 722 #if 0 /* XXX not needed, we don't have instructions in DMA buffers */ 723 pa = kvtop(addr); 724 for (end = pa + len; pa < end; pa += inc) 725 __asm volatile("icbi 0,%0" :: "r"(pa)); 726 __asm volatile("isync"); 727 #endif 728 729 return 0; 730 } 731