1/* 2 * Copyright (C) 1998, 1999 Jesper Skov 3 * 4 * Basically what is needed to replace functionality found in 5 * arch/m68k allowing Amiga drivers to work under APUS. 6 * Bits of code and/or ideas from arch/m68k and arch/ppc files. 7 * 8 * TODO: 9 * This file needs a *really* good cleanup. Restructure and optimize. 10 * Make sure it can be compiled for non-APUS configs. Begin to move 11 * Amiga specific stuff into mach/amiga. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/sched.h> 16#include <linux/init.h> 17#include <linux/initrd.h> 18#include <linux/seq_file.h> 19 20/* Needs INITSERIAL call in head.S! */ 21#undef APUS_DEBUG 22 23#include <asm/bootinfo.h> 24#include <asm/setup.h> 25#include <asm/amigahw.h> 26#include <asm/amigaints.h> 27#include <asm/amigappc.h> 28#include <asm/pgtable.h> 29#include <asm/dma.h> 30#include <asm/machdep.h> 31#include <asm/time.h> 32 33unsigned long m68k_machtype; 34char debug_device[6] = ""; 35 36extern void amiga_init_IRQ(void); 37 38extern void apus_setup_pci_ptrs(void); 39 40void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initdata = NULL; 41/* machine dependent irq functions */ 42void (*mach_init_IRQ) (void) __initdata = NULL; 43void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; 44void (*mach_get_model) (char *model) = NULL; 45int (*mach_get_hardware_list) (char *buffer) = NULL; 46int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; 47void (*mach_process_int) (int, struct pt_regs *) = NULL; 48/* machine dependent timer functions */ 49unsigned long (*mach_gettimeoffset) (void); 50void (*mach_gettod) (int*, int*, int*, int*, int*, int*); 51int (*mach_hwclk) (int, struct hwclk_time*) = NULL; 52int (*mach_set_clock_mmss) (unsigned long) = NULL; 53void (*mach_reset)( void ); 54long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ 55#ifdef CONFIG_HEARTBEAT 56void (*mach_heartbeat) (int) = NULL; 57extern void apus_heartbeat (void); 58#endif 59 60extern unsigned long amiga_model; 61extern unsigned decrementer_count;/* count value for 1e6/HZ microseconds */ 62extern unsigned count_period_num; /* 1 decrementer count equals */ 63extern unsigned count_period_den; /* count_period_num / count_period_den us */ 64 65int num_memory = 0; 66struct mem_info memory[NUM_MEMINFO];/* memory description */ 67int m68k_realnum_memory = 0; 68struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */ 69 70struct mem_info ramdisk; 71 72extern void config_amiga(void); 73 74static int __60nsram = 0; 75 76/* for cpuinfo */ 77static int __bus_speed = 0; 78static int __speed_test_failed = 0; 79 80/********************************************** COMPILE PROTECTION */ 81/* Provide some stubs that links to Amiga specific functions. 82 * This allows CONFIG_APUS to be removed from generic PPC files while 83 * preventing link errors for other PPC targets. 84 */ 85unsigned long apus_get_rtc_time(void) 86{ 87#ifdef CONFIG_APUS 88 extern unsigned long m68k_get_rtc_time(void); 89 90 return m68k_get_rtc_time (); 91#else 92 return 0; 93#endif 94} 95 96int apus_set_rtc_time(unsigned long nowtime) 97{ 98#ifdef CONFIG_APUS 99 extern int m68k_set_rtc_time(unsigned long nowtime); 100 101 return m68k_set_rtc_time (nowtime); 102#else 103 return 0; 104#endif 105} 106 107/*********************************************************** SETUP */ 108/* From arch/m68k/kernel/setup.c. */ 109void __init apus_setup_arch(void) 110{ 111#ifdef CONFIG_APUS 112 extern char cmd_line[]; 113 int i; 114 char *p, *q; 115 116 /* Let m68k-shared code know it should do the Amiga thing. */ 117 m68k_machtype = MACH_AMIGA; 118 119 for( p = cmd_line; p && *p; ) { 120 i = 0; 121 if (!strncmp( p, "debug=", 6 )) { 122 strlcpy( debug_device, p+6, sizeof(debug_device) ); 123 if ((q = strchr( debug_device, ' ' ))) *q = 0; 124 i = 1; 125 } else if (!strncmp( p, "60nsram", 7 )) { 126 APUS_WRITE (APUS_REG_WAITSTATE, 127 REGWAITSTATE_SETRESET 128 |REGWAITSTATE_PPCR 129 |REGWAITSTATE_PPCW); 130 __60nsram = 1; 131 i = 1; 132 } 133 134 if (i) { 135 /* option processed, delete it */ 136 if ((q = strchr( p, ' ' ))) 137 strcpy( p, q+1 ); 138 else 139 *p = 0; 140 } else { 141 if ((p = strchr( p, ' ' ))) ++p; 142 } 143 } 144 145 config_amiga(); 146 147#endif 148} 149 150int 151apus_show_cpuinfo(struct seq_file *m) 152{ 153 extern int __map_without_bats; 154 extern unsigned long powerup_PCI_present; 155 156 seq_printf(m, "machine\t\t: Amiga\n"); 157 seq_printf(m, "bus speed\t: %d%s", __bus_speed, 158 (__speed_test_failed) ? " [failed]\n" : "\n"); 159 seq_printf(m, "using BATs\t: %s\n", 160 (__map_without_bats) ? "No" : "Yes"); 161 seq_printf(m, "ram speed\t: %dns\n", (__60nsram) ? 60 : 70); 162 seq_printf(m, "PCI bridge\t: %s\n", 163 (powerup_PCI_present) ? "Yes" : "No"); 164 return 0; 165} 166 167static void get_current_tb(unsigned long long *time) 168{ 169 __asm __volatile ("1:mftbu 4 \n\t" 170 " mftb 5 \n\t" 171 " mftbu 6 \n\t" 172 " cmpw 4,6 \n\t" 173 " bne 1b \n\t" 174 " stw 4,0(%0)\n\t" 175 " stw 5,4(%0)\n\t" 176 : 177 : "r" (time) 178 : "r4", "r5", "r6"); 179} 180 181 182void apus_calibrate_decr(void) 183{ 184#ifdef CONFIG_APUS 185 unsigned long freq; 186 187 /* This algorithm for determining the bus speed was 188 contributed by Ralph Schmidt. */ 189 unsigned long long start, stop; 190 int bus_speed; 191 int speed_test_failed = 0; 192 193 { 194 unsigned long loop = amiga_eclock / 10; 195 196 get_current_tb (&start); 197 while (loop--) { 198 unsigned char tmp; 199 200 tmp = ciaa.pra; 201 } 202 get_current_tb (&stop); 203 } 204 205 bus_speed = (((unsigned long)(stop-start))*10*4) / 1000000; 206 if (AMI_1200 == amiga_model) 207 bus_speed /= 2; 208 209 if ((bus_speed >= 47) && (bus_speed < 53)) { 210 bus_speed = 50; 211 freq = 12500000; 212 } else if ((bus_speed >= 57) && (bus_speed < 63)) { 213 bus_speed = 60; 214 freq = 15000000; 215 } else if ((bus_speed >= 63) && (bus_speed < 69)) { 216 bus_speed = 67; 217 freq = 16666667; 218 } else { 219 printk ("APUS: Unable to determine bus speed (%d). " 220 "Defaulting to 50MHz", bus_speed); 221 bus_speed = 50; 222 freq = 12500000; 223 speed_test_failed = 1; 224 } 225 226 /* Ease diagnostics... */ 227 { 228 extern int __map_without_bats; 229 extern unsigned long powerup_PCI_present; 230 231 printk ("APUS: BATs=%d, BUS=%dMHz", 232 (__map_without_bats) ? 0 : 1, 233 bus_speed); 234 if (speed_test_failed) 235 printk ("[FAILED - please report]"); 236 237 printk (", RAM=%dns, PCI bridge=%d\n", 238 (__60nsram) ? 60 : 70, 239 (powerup_PCI_present) ? 1 : 0); 240 241 /* print a bit more if asked politely... */ 242 if (!(ciaa.pra & 0x40)){ 243 extern unsigned int bat_addrs[4][3]; 244 int b; 245 for (b = 0; b < 4; ++b) { 246 printk ("APUS: BAT%d ", b); 247 printk ("%08x-%08x -> %08x\n", 248 bat_addrs[b][0], 249 bat_addrs[b][1], 250 bat_addrs[b][2]); 251 } 252 } 253 254 } 255 256 printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", 257 freq/1000000, freq%1000000); 258 tb_ticks_per_jiffy = freq / HZ; 259 tb_to_us = mulhwu_scale_factor(freq, 1000000); 260 261 __bus_speed = bus_speed; 262 __speed_test_failed = speed_test_failed; 263#endif 264} 265 266void arch_gettod(int *year, int *mon, int *day, int *hour, 267 int *min, int *sec) 268{ 269#ifdef CONFIG_APUS 270 if (mach_gettod) 271 mach_gettod(year, mon, day, hour, min, sec); 272 else 273 *year = *mon = *day = *hour = *min = *sec = 0; 274#endif 275} 276 277/* for "kbd-reset" cmdline param */ 278__init 279void kbd_reset_setup(char *str, int *ints) 280{ 281} 282 283/*********************************************************** MEMORY */ 284#define KMAP_MAX 32 285unsigned long kmap_chunks[KMAP_MAX*3]; 286int kmap_chunk_count = 0; 287 288/* From pgtable.h */ 289static __inline__ pte_t *my_find_pte(struct mm_struct *mm,unsigned long va) 290{ 291 pgd_t *dir = 0; 292 pmd_t *pmd = 0; 293 pte_t *pte = 0; 294 295 va &= PAGE_MASK; 296 297 dir = pgd_offset( mm, va ); 298 if (dir) 299 { 300 pmd = pmd_offset(dir, va & PAGE_MASK); 301 if (pmd && pmd_present(*pmd)) 302 { 303 pte = pte_offset(pmd, va); 304 } 305 } 306 return pte; 307} 308 309 310/* Again simulating an m68k/mm/kmap.c function. */ 311void kernel_set_cachemode( unsigned long address, unsigned long size, 312 unsigned int cmode ) 313{ 314 unsigned long mask, flags; 315 316 switch (cmode) 317 { 318 case IOMAP_FULL_CACHING: 319 mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); 320 flags = 0; 321 break; 322 case IOMAP_NOCACHE_SER: 323 mask = ~0; 324 flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); 325 break; 326 default: 327 panic ("kernel_set_cachemode() doesn't support mode %d\n", 328 cmode); 329 break; 330 } 331 332 size /= PAGE_SIZE; 333 address &= PAGE_MASK; 334 while (size--) 335 { 336 pte_t *pte; 337 338 pte = my_find_pte(&init_mm, address); 339 if ( !pte ) 340 { 341 printk("pte NULL in kernel_set_cachemode()\n"); 342 return; 343 } 344 345 pte_val (*pte) &= mask; 346 pte_val (*pte) |= flags; 347 flush_tlb_page(find_vma(&init_mm,address),address); 348 349 address += PAGE_SIZE; 350 } 351} 352 353unsigned long mm_ptov (unsigned long paddr) 354{ 355 unsigned long ret; 356 if (paddr < 16*1024*1024) 357 ret = ZTWO_VADDR(paddr); 358 else { 359 int i; 360 361 for (i = 0; i < kmap_chunk_count;){ 362 unsigned long phys = kmap_chunks[i++]; 363 unsigned long size = kmap_chunks[i++]; 364 unsigned long virt = kmap_chunks[i++]; 365 if (paddr >= phys 366 && paddr < (phys + size)){ 367 ret = virt + paddr - phys; 368 goto exit; 369 } 370 } 371 372 ret = (unsigned long) __va(paddr); 373 } 374exit: 375#ifdef DEBUGPV 376 printk ("PTOV(%lx)=%lx\n", paddr, ret); 377#endif 378 return ret; 379} 380 381int mm_end_of_chunk (unsigned long addr, int len) 382{ 383 if (memory[0].addr + memory[0].size == addr + len) 384 return 1; 385 return 0; 386} 387 388/*********************************************************** CACHE */ 389 390#define L1_CACHE_BYTES 32 391#define MAX_CACHE_SIZE 8192 392void cache_push(__u32 addr, int length) 393{ 394 addr = mm_ptov(addr); 395 396 if (MAX_CACHE_SIZE < length) 397 length = MAX_CACHE_SIZE; 398 399 while(length > 0){ 400 __asm ("dcbf 0,%0\n\t" 401 : : "r" (addr)); 402 addr += L1_CACHE_BYTES; 403 length -= L1_CACHE_BYTES; 404 } 405 /* Also flush trailing block */ 406 __asm ("dcbf 0,%0\n\t" 407 "sync \n\t" 408 : : "r" (addr)); 409} 410 411void cache_clear(__u32 addr, int length) 412{ 413 if (MAX_CACHE_SIZE < length) 414 length = MAX_CACHE_SIZE; 415 416 addr = mm_ptov(addr); 417 418 __asm ("dcbf 0,%0\n\t" 419 "sync \n\t" 420 "icbi 0,%0 \n\t" 421 "isync \n\t" 422 : : "r" (addr)); 423 424 addr += L1_CACHE_BYTES; 425 length -= L1_CACHE_BYTES; 426 427 while(length > 0){ 428 __asm ("dcbf 0,%0\n\t" 429 "sync \n\t" 430 "icbi 0,%0 \n\t" 431 "isync \n\t" 432 : : "r" (addr)); 433 addr += L1_CACHE_BYTES; 434 length -= L1_CACHE_BYTES; 435 } 436 437 __asm ("dcbf 0,%0\n\t" 438 "sync \n\t" 439 "icbi 0,%0 \n\t" 440 "isync \n\t" 441 : : "r" (addr)); 442} 443 444/****************************************************** from setup.c */ 445void 446apus_restart(char *cmd) 447{ 448 local_irq_disable(); 449 450 APUS_WRITE(APUS_REG_LOCK, 451 REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); 452 APUS_WRITE(APUS_REG_LOCK, 453 REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); 454 APUS_WRITE(APUS_REG_LOCK, 455 REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); 456 APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); 457 APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); 458 for(;;); 459} 460 461void 462apus_power_off(void) 463{ 464 for (;;); 465} 466 467void 468apus_halt(void) 469{ 470 apus_restart(NULL); 471} 472 473/****************************************************** IRQ stuff */ 474 475static unsigned char last_ipl[8]; 476 477int apus_get_irq(void) 478{ 479 unsigned char ipl_emu, mask; 480 unsigned int level; 481 482 APUS_READ(APUS_IPL_EMU, ipl_emu); 483 level = (ipl_emu >> 3) & IPLEMU_IPLMASK; 484 mask = IPLEMU_SETRESET|IPLEMU_DISABLEINT|level; 485 level ^= 7; 486 487 /* Save previous IPL value */ 488 if (last_ipl[level]) 489 return -2; 490 last_ipl[level] = ipl_emu; 491 492 /* Set to current IPL value */ 493 APUS_WRITE(APUS_IPL_EMU, mask); 494 APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|level); 495 496 497#ifdef __INTERRUPT_DEBUG 498 printk("<%d:%d>", level, ~ipl_emu & IPLEMU_IPLMASK); 499#endif 500 return level + IRQ_AMIGA_AUTO; 501} 502 503void apus_end_irq(unsigned int irq) 504{ 505 unsigned char ipl_emu; 506 unsigned int level = irq - IRQ_AMIGA_AUTO; 507#ifdef __INTERRUPT_DEBUG 508 printk("{%d}", ~last_ipl[level] & IPLEMU_IPLMASK); 509#endif 510 /* Restore IPL to the previous value */ 511 ipl_emu = last_ipl[level] & IPLEMU_IPLMASK; 512 APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET|IPLEMU_DISABLEINT|ipl_emu); 513 last_ipl[level] = 0; 514 ipl_emu ^= 7; 515 APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|ipl_emu); 516} 517 518/****************************************************** debugging */ 519 520/* some serial hardware definitions */ 521#define SDR_OVRUN (1<<15) 522#define SDR_RBF (1<<14) 523#define SDR_TBE (1<<13) 524#define SDR_TSRE (1<<12) 525 526#define AC_SETCLR (1<<15) 527#define AC_UARTBRK (1<<11) 528 529#define SER_DTR (1<<7) 530#define SER_RTS (1<<6) 531#define SER_DCD (1<<5) 532#define SER_CTS (1<<4) 533#define SER_DSR (1<<3) 534 535static __inline__ void ser_RTSon(void) 536{ 537 ciab.pra &= ~SER_RTS; /* active low */ 538} 539 540int __debug_ser_out( unsigned char c ) 541{ 542 amiga_custom.serdat = c | 0x100; 543 mb(); 544 while (!(amiga_custom.serdatr & 0x2000)) 545 barrier(); 546 return 1; 547} 548 549unsigned char __debug_ser_in( void ) 550{ 551 unsigned char c; 552 553 while( !(amiga_custom.intreqr & IF_RBF) ) 554 barrier(); 555 c = amiga_custom.serdatr; 556 /* clear the interrupt, so that another character can be read */ 557 amiga_custom.intreq = IF_RBF; 558 return c; 559} 560 561int __debug_serinit( void ) 562{ 563 unsigned long flags; 564 565 local_irq_save(flags); 566 567 /* turn off Rx and Tx interrupts */ 568 amiga_custom.intena = IF_RBF | IF_TBE; 569 570 /* clear any pending interrupt */ 571 amiga_custom.intreq = IF_RBF | IF_TBE; 572 573 local_irq_restore(flags); 574 575 /* 576 * set the appropriate directions for the modem control flags, 577 * and clear RTS and DTR 578 */ 579 ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ 580 ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ 581 582#ifdef CONFIG_KGDB 583 /* turn Rx interrupts on for GDB */ 584 amiga_custom.intena = IF_SETCLR | IF_RBF; 585 ser_RTSon(); 586#endif 587 588 return 0; 589} 590 591void __debug_print_hex(unsigned long x) 592{ 593 int i; 594 char hexchars[] = "0123456789ABCDEF"; 595 596 for (i = 0; i < 8; i++) { 597 __debug_ser_out(hexchars[(x >> 28) & 15]); 598 x <<= 4; 599 } 600 __debug_ser_out('\n'); 601 __debug_ser_out('\r'); 602} 603 604void __debug_print_string(char* s) 605{ 606 unsigned char c; 607 while((c = *s++)) 608 __debug_ser_out(c); 609 __debug_ser_out('\n'); 610 __debug_ser_out('\r'); 611} 612 613static void apus_progress(char *s, unsigned short value) 614{ 615 __debug_print_string(s); 616} 617 618/****************************************************** init */ 619 620/* The number of spurious interrupts */ 621volatile unsigned int num_spurious; 622 623extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; 624 625 626extern void amiga_enable_irq(unsigned int irq); 627extern void amiga_disable_irq(unsigned int irq); 628 629struct hw_interrupt_type amiga_sys_irqctrl = { 630 .typename = "Amiga IPL", 631 .end = apus_end_irq, 632}; 633 634struct hw_interrupt_type amiga_irqctrl = { 635 .typename = "Amiga ", 636 .enable = amiga_enable_irq, 637 .disable = amiga_disable_irq, 638}; 639 640#define HARDWARE_MAPPED_SIZE (512*1024) 641unsigned long __init apus_find_end_of_memory(void) 642{ 643 int shadow = 0; 644 unsigned long total; 645 646 /* The memory size reported by ADOS excludes the 512KB 647 reserved for PPC exception registers and possibly 512KB 648 containing a shadow of the ADOS ROM. */ 649 { 650 unsigned long size = memory[0].size; 651 652 /* If 2MB aligned, size was probably user 653 specified. We can't tell anything about shadowing 654 in this case so skip shadow assignment. */ 655 if (0 != (size & 0x1fffff)){ 656 /* Align to 512KB to ensure correct handling 657 of both memfile and system specified 658 sizes. */ 659 size = ((size+0x0007ffff) & 0xfff80000); 660 /* If memory is 1MB aligned, assume 661 shadowing. */ 662 shadow = !(size & 0x80000); 663 } 664 665 /* Add the chunk that ADOS does not see. by aligning 666 the size to the nearest 2MB limit upwards. */ 667 memory[0].size = ((size+0x001fffff) & 0xffe00000); 668 } 669 670 ppc_memstart = memory[0].addr; 671 ppc_memoffset = PAGE_OFFSET - PPC_MEMSTART; 672 total = memory[0].size; 673 674 /* Remove the memory chunks that are controlled by special 675 Phase5 hardware. */ 676 677 if (shadow) 678 total -= HARDWARE_MAPPED_SIZE; 679 680 /* Remove the upper 512KB where the PPC exception 681 vectors are mapped. */ 682 total -= HARDWARE_MAPPED_SIZE; 683 684 /* Linux/APUS only handles one block of memory -- the one on 685 the PowerUP board. Other system memory is horrible slow in 686 comparison. The user can use other memory for swapping 687 using the z2ram device. */ 688 return total; 689} 690 691static void __init 692apus_map_io(void) 693{ 694 /* Map PPC exception vectors. */ 695 io_block_mapping(0xfff00000, 0xfff00000, 0x00020000, _PAGE_KERNEL); 696 /* Map chip and ZorroII memory */ 697 io_block_mapping(zTwoBase, 0x00000000, 0x01000000, _PAGE_IO); 698} 699 700__init 701void apus_init_IRQ(void) 702{ 703 struct irqaction *action; 704 int i; 705 706#ifdef CONFIG_PCI 707 apus_setup_pci_ptrs(); 708#endif 709 710 for ( i = 0 ; i < AMI_IRQS; i++ ) { 711 irq_desc[i].status = IRQ_LEVEL; 712 if (i < IRQ_AMIGA_AUTO) { 713 irq_desc[i].chip = &amiga_irqctrl; 714 } else { 715 irq_desc[i].chip = &amiga_sys_irqctrl; 716 action = &amiga_sys_irqaction[i-IRQ_AMIGA_AUTO]; 717 if (action->name) 718 setup_irq(i, action); 719 } 720 } 721 722 amiga_init_IRQ(); 723 724} 725 726__init 727void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, 728 unsigned long r6, unsigned long r7) 729{ 730 extern int parse_bootinfo(const struct bi_record *); 731 extern char _end[]; 732 733 /* Parse bootinfo. The bootinfo is located right after 734 the kernel bss */ 735 parse_bootinfo((const struct bi_record *)&_end); 736#ifdef CONFIG_BLK_DEV_INITRD 737 /* Take care of initrd if we have one. Use data from 738 bootinfo to avoid the need to initialize PPC 739 registers when kernel is booted via a PPC reset. */ 740 if ( ramdisk.addr ) { 741 initrd_start = (unsigned long) __va(ramdisk.addr); 742 initrd_end = (unsigned long) 743 __va(ramdisk.size + ramdisk.addr); 744 } 745#endif /* CONFIG_BLK_DEV_INITRD */ 746 747 ISA_DMA_THRESHOLD = 0x00ffffff; 748 749 ppc_md.setup_arch = apus_setup_arch; 750 ppc_md.show_cpuinfo = apus_show_cpuinfo; 751 ppc_md.init_IRQ = apus_init_IRQ; 752 ppc_md.get_irq = apus_get_irq; 753 754#ifdef CONFIG_HEARTBEAT 755 ppc_md.heartbeat = apus_heartbeat; 756 ppc_md.heartbeat_count = 1; 757#endif 758#ifdef APUS_DEBUG 759 __debug_serinit(); 760 ppc_md.progress = apus_progress; 761#endif 762 ppc_md.init = NULL; 763 764 ppc_md.restart = apus_restart; 765 ppc_md.power_off = apus_power_off; 766 ppc_md.halt = apus_halt; 767 768 ppc_md.time_init = NULL; 769 ppc_md.set_rtc_time = apus_set_rtc_time; 770 ppc_md.get_rtc_time = apus_get_rtc_time; 771 ppc_md.calibrate_decr = apus_calibrate_decr; 772 773 ppc_md.find_end_of_memory = apus_find_end_of_memory; 774 ppc_md.setup_io_mappings = apus_map_io; 775} 776