Commit dbdd25065e90ba78c01b03d238785f25476c04a1
1 parent
d68f1306
Implement time-base start/stop helpers.
Implement PowerPC 6xx time-base enable input pin. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3394 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
92 additions
and
29 deletions
hw/ppc.c
| ... | ... | @@ -30,6 +30,9 @@ |
| 30 | 30 | extern FILE *logfile; |
| 31 | 31 | extern int loglevel; |
| 32 | 32 | |
| 33 | +static void cpu_ppc_tb_stop (CPUState *env); | |
| 34 | +static void cpu_ppc_tb_start (CPUState *env); | |
| 35 | + | |
| 33 | 36 | static void ppc_set_irq (CPUState *env, int n_IRQ, int level) |
| 34 | 37 | { |
| 35 | 38 | if (level) { |
| ... | ... | @@ -65,6 +68,19 @@ static void ppc6xx_set_irq (void *opaque, int pin, int level) |
| 65 | 68 | /* Don't generate spurious events */ |
| 66 | 69 | if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { |
| 67 | 70 | switch (pin) { |
| 71 | + case PPC6xx_INPUT_TBEN: | |
| 72 | + /* Level sensitive - active high */ | |
| 73 | +#if defined(PPC_DEBUG_IRQ) | |
| 74 | + if (loglevel & CPU_LOG_INT) { | |
| 75 | + fprintf(logfile, "%s: %s the time base\n", | |
| 76 | + __func__, level ? "start" : "stop"); | |
| 77 | + } | |
| 78 | +#endif | |
| 79 | + if (level) { | |
| 80 | + cpu_ppc_tb_start(env); | |
| 81 | + } else { | |
| 82 | + cpu_ppc_tb_stop(env); | |
| 83 | + } | |
| 68 | 84 | case PPC6xx_INPUT_INT: |
| 69 | 85 | /* Level sensitive - active high */ |
| 70 | 86 | #if defined(PPC_DEBUG_IRQ) |
| ... | ... | @@ -402,11 +418,12 @@ void ppc40x_irq_init (CPUState *env) |
| 402 | 418 | /* PowerPC time base and decrementer emulation */ |
| 403 | 419 | struct ppc_tb_t { |
| 404 | 420 | /* Time base management */ |
| 405 | - int64_t tb_offset; /* Compensation */ | |
| 406 | - int64_t atb_offset; /* Compensation */ | |
| 407 | - uint32_t tb_freq; /* TB frequency */ | |
| 421 | + int64_t tb_offset; /* Compensation */ | |
| 422 | + int64_t atb_offset; /* Compensation */ | |
| 423 | + uint32_t tb_freq; /* TB frequency */ | |
| 408 | 424 | /* Decrementer management */ |
| 409 | - uint64_t decr_next; /* Tick for next decr interrupt */ | |
| 425 | + uint64_t decr_next; /* Tick for next decr interrupt */ | |
| 426 | + uint32_t decr_freq; /* decrementer frequency */ | |
| 410 | 427 | struct QEMUTimer *decr_timer; |
| 411 | 428 | #if defined(TARGET_PPC64H) |
| 412 | 429 | /* Hypervisor decrementer management */ |
| ... | ... | @@ -418,12 +435,11 @@ struct ppc_tb_t { |
| 418 | 435 | void *opaque; |
| 419 | 436 | }; |
| 420 | 437 | |
| 421 | -static always_inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env, | |
| 438 | +static always_inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env, uint64_t vmclk, | |
| 422 | 439 | int64_t tb_offset) |
| 423 | 440 | { |
| 424 | 441 | /* TB time in tb periods */ |
| 425 | - return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset, | |
| 426 | - tb_env->tb_freq, ticks_per_sec); | |
| 442 | + return muldiv64(vmclk, tb_env->tb_freq, ticks_per_sec) + tb_offset; | |
| 427 | 443 | } |
| 428 | 444 | |
| 429 | 445 | uint32_t cpu_ppc_load_tbl (CPUState *env) |
| ... | ... | @@ -431,7 +447,7 @@ uint32_t cpu_ppc_load_tbl (CPUState *env) |
| 431 | 447 | ppc_tb_t *tb_env = env->tb_env; |
| 432 | 448 | uint64_t tb; |
| 433 | 449 | |
| 434 | - tb = cpu_ppc_get_tb(tb_env, tb_env->tb_offset); | |
| 450 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); | |
| 435 | 451 | #if defined(PPC_DEBUG_TB) |
| 436 | 452 | if (loglevel != 0) { |
| 437 | 453 | fprintf(logfile, "%s: tb=0x%016lx\n", __func__, tb); |
| ... | ... | @@ -446,7 +462,7 @@ static always_inline uint32_t _cpu_ppc_load_tbu (CPUState *env) |
| 446 | 462 | ppc_tb_t *tb_env = env->tb_env; |
| 447 | 463 | uint64_t tb; |
| 448 | 464 | |
| 449 | - tb = cpu_ppc_get_tb(tb_env, tb_env->tb_offset); | |
| 465 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); | |
| 450 | 466 | #if defined(PPC_DEBUG_TB) |
| 451 | 467 | if (loglevel != 0) { |
| 452 | 468 | fprintf(logfile, "%s: tb=0x%016lx\n", __func__, tb); |
| ... | ... | @@ -461,12 +477,11 @@ uint32_t cpu_ppc_load_tbu (CPUState *env) |
| 461 | 477 | return _cpu_ppc_load_tbu(env); |
| 462 | 478 | } |
| 463 | 479 | |
| 464 | -static always_inline void cpu_ppc_store_tb (ppc_tb_t *tb_env, | |
| 480 | +static always_inline void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t vmclk, | |
| 465 | 481 | int64_t *tb_offsetp, |
| 466 | 482 | uint64_t value) |
| 467 | 483 | { |
| 468 | - *tb_offsetp = muldiv64(value, ticks_per_sec, tb_env->tb_freq) | |
| 469 | - - qemu_get_clock(vm_clock); | |
| 484 | + *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, ticks_per_sec); | |
| 470 | 485 | #ifdef PPC_DEBUG_TB |
| 471 | 486 | if (loglevel != 0) { |
| 472 | 487 | fprintf(logfile, "%s: tb=0x%016lx offset=%08lx\n", __func__, value, |
| ... | ... | @@ -480,9 +495,10 @@ void cpu_ppc_store_tbl (CPUState *env, uint32_t value) |
| 480 | 495 | ppc_tb_t *tb_env = env->tb_env; |
| 481 | 496 | uint64_t tb; |
| 482 | 497 | |
| 483 | - tb = cpu_ppc_get_tb(tb_env, tb_env->tb_offset); | |
| 498 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); | |
| 484 | 499 | tb &= 0xFFFFFFFF00000000ULL; |
| 485 | - cpu_ppc_store_tb(tb_env, &tb_env->tb_offset, tb | (uint64_t)value); | |
| 500 | + cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), | |
| 501 | + &tb_env->tb_offset, tb | (uint64_t)value); | |
| 486 | 502 | } |
| 487 | 503 | |
| 488 | 504 | static always_inline void _cpu_ppc_store_tbu (CPUState *env, uint32_t value) |
| ... | ... | @@ -490,10 +506,10 @@ static always_inline void _cpu_ppc_store_tbu (CPUState *env, uint32_t value) |
| 490 | 506 | ppc_tb_t *tb_env = env->tb_env; |
| 491 | 507 | uint64_t tb; |
| 492 | 508 | |
| 493 | - tb = cpu_ppc_get_tb(tb_env, tb_env->tb_offset); | |
| 509 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); | |
| 494 | 510 | tb &= 0x00000000FFFFFFFFULL; |
| 495 | - cpu_ppc_store_tb(tb_env, &tb_env->tb_offset, | |
| 496 | - ((uint64_t)value << 32) | tb); | |
| 511 | + cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), | |
| 512 | + &tb_env->tb_offset, ((uint64_t)value << 32) | tb); | |
| 497 | 513 | } |
| 498 | 514 | |
| 499 | 515 | void cpu_ppc_store_tbu (CPUState *env, uint32_t value) |
| ... | ... | @@ -506,7 +522,7 @@ uint32_t cpu_ppc_load_atbl (CPUState *env) |
| 506 | 522 | ppc_tb_t *tb_env = env->tb_env; |
| 507 | 523 | uint64_t tb; |
| 508 | 524 | |
| 509 | - tb = cpu_ppc_get_tb(tb_env, tb_env->atb_offset); | |
| 525 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); | |
| 510 | 526 | #if defined(PPC_DEBUG_TB) |
| 511 | 527 | if (loglevel != 0) { |
| 512 | 528 | fprintf(logfile, "%s: tb=0x%016lx\n", __func__, tb); |
| ... | ... | @@ -521,7 +537,7 @@ uint32_t cpu_ppc_load_atbu (CPUState *env) |
| 521 | 537 | ppc_tb_t *tb_env = env->tb_env; |
| 522 | 538 | uint64_t tb; |
| 523 | 539 | |
| 524 | - tb = cpu_ppc_get_tb(tb_env, tb_env->atb_offset); | |
| 540 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); | |
| 525 | 541 | #if defined(PPC_DEBUG_TB) |
| 526 | 542 | if (loglevel != 0) { |
| 527 | 543 | fprintf(logfile, "%s: tb=0x%016lx\n", __func__, tb); |
| ... | ... | @@ -536,9 +552,10 @@ void cpu_ppc_store_atbl (CPUState *env, uint32_t value) |
| 536 | 552 | ppc_tb_t *tb_env = env->tb_env; |
| 537 | 553 | uint64_t tb; |
| 538 | 554 | |
| 539 | - tb = cpu_ppc_get_tb(tb_env, tb_env->atb_offset); | |
| 555 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); | |
| 540 | 556 | tb &= 0xFFFFFFFF00000000ULL; |
| 541 | - cpu_ppc_store_tb(tb_env, &tb_env->atb_offset, tb | (uint64_t)value); | |
| 557 | + cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), | |
| 558 | + &tb_env->atb_offset, tb | (uint64_t)value); | |
| 542 | 559 | } |
| 543 | 560 | |
| 544 | 561 | void cpu_ppc_store_atbu (CPUState *env, uint32_t value) |
| ... | ... | @@ -546,10 +563,53 @@ void cpu_ppc_store_atbu (CPUState *env, uint32_t value) |
| 546 | 563 | ppc_tb_t *tb_env = env->tb_env; |
| 547 | 564 | uint64_t tb; |
| 548 | 565 | |
| 549 | - tb = cpu_ppc_get_tb(tb_env, tb_env->atb_offset); | |
| 566 | + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); | |
| 550 | 567 | tb &= 0x00000000FFFFFFFFULL; |
| 551 | - cpu_ppc_store_tb(tb_env, &tb_env->atb_offset, | |
| 552 | - ((uint64_t)value << 32) | tb); | |
| 568 | + cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), | |
| 569 | + &tb_env->atb_offset, ((uint64_t)value << 32) | tb); | |
| 570 | +} | |
| 571 | + | |
| 572 | +static void cpu_ppc_tb_stop (CPUState *env) | |
| 573 | +{ | |
| 574 | + ppc_tb_t *tb_env = env->tb_env; | |
| 575 | + uint64_t tb, atb, vmclk; | |
| 576 | + | |
| 577 | + /* If the time base is already frozen, do nothing */ | |
| 578 | + if (tb_env->tb_freq != 0) { | |
| 579 | + vmclk = qemu_get_clock(vm_clock); | |
| 580 | + /* Get the time base */ | |
| 581 | + tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); | |
| 582 | + /* Get the alternate time base */ | |
| 583 | + atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset); | |
| 584 | + /* Store the time base value (ie compute the current offset) */ | |
| 585 | + cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); | |
| 586 | + /* Store the alternate time base value (compute the current offset) */ | |
| 587 | + cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); | |
| 588 | + /* Set the time base frequency to zero */ | |
| 589 | + tb_env->tb_freq = 0; | |
| 590 | + /* Now, the time bases are frozen to tb_offset / atb_offset value */ | |
| 591 | + } | |
| 592 | +} | |
| 593 | + | |
| 594 | +static void cpu_ppc_tb_start (CPUState *env) | |
| 595 | +{ | |
| 596 | + ppc_tb_t *tb_env = env->tb_env; | |
| 597 | + uint64_t tb, atb, vmclk; | |
| 598 | + | |
| 599 | + /* If the time base is not frozen, do nothing */ | |
| 600 | + if (tb_env->tb_freq == 0) { | |
| 601 | + vmclk = qemu_get_clock(vm_clock); | |
| 602 | + /* Get the time base from tb_offset */ | |
| 603 | + tb = tb_env->tb_offset; | |
| 604 | + /* Get the alternate time base from atb_offset */ | |
| 605 | + atb = tb_env->atb_offset; | |
| 606 | + /* Restore the tb frequency from the decrementer frequency */ | |
| 607 | + tb_env->tb_freq = tb_env->decr_freq; | |
| 608 | + /* Store the time base value */ | |
| 609 | + cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); | |
| 610 | + /* Store the alternate time base value */ | |
| 611 | + cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); | |
| 612 | + } | |
| 553 | 613 | } |
| 554 | 614 | |
| 555 | 615 | static always_inline uint32_t _cpu_ppc_load_decr (CPUState *env, |
| ... | ... | @@ -561,9 +621,9 @@ static always_inline uint32_t _cpu_ppc_load_decr (CPUState *env, |
| 561 | 621 | |
| 562 | 622 | diff = tb_env->decr_next - qemu_get_clock(vm_clock); |
| 563 | 623 | if (diff >= 0) |
| 564 | - decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); | |
| 624 | + decr = muldiv64(diff, tb_env->decr_freq, ticks_per_sec); | |
| 565 | 625 | else |
| 566 | - decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); | |
| 626 | + decr = -muldiv64(-diff, tb_env->decr_freq, ticks_per_sec); | |
| 567 | 627 | #if defined(PPC_DEBUG_TB) |
| 568 | 628 | if (loglevel != 0) { |
| 569 | 629 | fprintf(logfile, "%s: 0x%08x\n", __func__, decr); |
| ... | ... | @@ -639,7 +699,7 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, |
| 639 | 699 | } |
| 640 | 700 | #endif |
| 641 | 701 | now = qemu_get_clock(vm_clock); |
| 642 | - next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq); | |
| 702 | + next = now + muldiv64(value, ticks_per_sec, tb_env->decr_freq); | |
| 643 | 703 | if (is_excp) |
| 644 | 704 | next += *nextp - now; |
| 645 | 705 | if (next == now) |
| ... | ... | @@ -708,6 +768,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) |
| 708 | 768 | ppc_tb_t *tb_env = env->tb_env; |
| 709 | 769 | |
| 710 | 770 | tb_env->tb_freq = freq; |
| 771 | + tb_env->decr_freq = freq; | |
| 711 | 772 | /* There is a bug in Linux 2.4 kernels: |
| 712 | 773 | * if a decrementer exception is pending when it enables msr_ee at startup, |
| 713 | 774 | * it's not ready to handle it... |
| ... | ... | @@ -848,7 +909,7 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) |
| 848 | 909 | #endif |
| 849 | 910 | now = qemu_get_clock(vm_clock); |
| 850 | 911 | next = now + muldiv64(ppcemb_timer->pit_reload, |
| 851 | - ticks_per_sec, tb_env->tb_freq); | |
| 912 | + ticks_per_sec, tb_env->decr_freq); | |
| 852 | 913 | if (is_excp) |
| 853 | 914 | next += tb_env->decr_next - now; |
| 854 | 915 | if (next == now) |
| ... | ... | @@ -912,7 +973,7 @@ static void cpu_4xx_wdt_cb (void *opaque) |
| 912 | 973 | /* Cannot occur, but makes gcc happy */ |
| 913 | 974 | return; |
| 914 | 975 | } |
| 915 | - next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq); | |
| 976 | + next = now + muldiv64(next, ticks_per_sec, tb_env->decr_freq); | |
| 916 | 977 | if (next == now) |
| 917 | 978 | next++; |
| 918 | 979 | #ifdef PPC_DEBUG_TB |
| ... | ... | @@ -1014,6 +1075,7 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) |
| 1014 | 1075 | } |
| 1015 | 1076 | #endif |
| 1016 | 1077 | tb_env->tb_freq = freq; |
| 1078 | + tb_env->decr_freq = freq; | |
| 1017 | 1079 | /* XXX: we should also update all timers */ |
| 1018 | 1080 | } |
| 1019 | 1081 | |
| ... | ... | @@ -1029,6 +1091,7 @@ clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq) |
| 1029 | 1091 | env->tb_env = tb_env; |
| 1030 | 1092 | ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t)); |
| 1031 | 1093 | tb_env->tb_freq = freq; |
| 1094 | + tb_env->decr_freq = freq; | |
| 1032 | 1095 | tb_env->opaque = ppcemb_timer; |
| 1033 | 1096 | #ifdef PPC_DEBUG_TB |
| 1034 | 1097 | if (loglevel != 0) { | ... | ... |