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) { | ... | ... |