Commit 4b6d0a4c7a3d029cdc4bf6520280275814f501c9
1 parent
e9ebed4d
PowerPC embedded timers fixes.
Improve PowerPC timers debug. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2715 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
101 additions
and
53 deletions
hw/ppc.c
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | #include "m48t59.h" |
26 | 26 | |
27 | 27 | //#define PPC_DEBUG_IRQ |
28 | +//#define PPC_DEBUG_TB | |
28 | 29 | |
29 | 30 | extern FILE *logfile; |
30 | 31 | extern int loglevel; |
... | ... | @@ -404,8 +405,6 @@ void ppc405_irq_init (CPUState *env) |
404 | 405 | |
405 | 406 | /*****************************************************************************/ |
406 | 407 | /* PowerPC time base and decrementer emulation */ |
407 | -//#define DEBUG_TB | |
408 | - | |
409 | 408 | struct ppc_tb_t { |
410 | 409 | /* Time base management */ |
411 | 410 | int64_t tb_offset; /* Compensation */ |
... | ... | @@ -429,14 +428,14 @@ uint32_t cpu_ppc_load_tbl (CPUState *env) |
429 | 428 | uint64_t tb; |
430 | 429 | |
431 | 430 | tb = cpu_ppc_get_tb(tb_env); |
432 | -#ifdef DEBUG_TB | |
431 | +#ifdef PPC_DEBUG_TB | |
433 | 432 | { |
434 | 433 | static int last_time; |
435 | 434 | int now; |
436 | 435 | now = time(NULL); |
437 | 436 | if (last_time != now) { |
438 | 437 | last_time = now; |
439 | - if (loglevel) { | |
438 | + if (loglevel != 0) { | |
440 | 439 | fprintf(logfile, "%s: tb=0x%016lx %d %08lx\n", |
441 | 440 | __func__, tb, now, tb_env->tb_offset); |
442 | 441 | } |
... | ... | @@ -453,8 +452,8 @@ uint32_t cpu_ppc_load_tbu (CPUState *env) |
453 | 452 | uint64_t tb; |
454 | 453 | |
455 | 454 | tb = cpu_ppc_get_tb(tb_env); |
456 | -#ifdef DEBUG_TB | |
457 | - if (loglevel) { | |
455 | +#if defined(PPC_DEBUG_TB) | |
456 | + if (loglevel != 0) { | |
458 | 457 | fprintf(logfile, "%s: tb=0x%016lx\n", __func__, tb); |
459 | 458 | } |
460 | 459 | #endif |
... | ... | @@ -466,9 +465,10 @@ static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value) |
466 | 465 | { |
467 | 466 | tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq) |
468 | 467 | - qemu_get_clock(vm_clock); |
469 | -#ifdef DEBUG_TB | |
470 | - if (loglevel) { | |
471 | - fprintf(logfile, "%s: tb=0x%016lx offset=%08x\n", __func__, value); | |
468 | +#ifdef PPC_DEBUG_TB | |
469 | + if (loglevel != 0) { | |
470 | + fprintf(logfile, "%s: tb=0x%016lx offset=%08lx\n", __func__, value, | |
471 | + tb_env->tb_offset); | |
472 | 472 | } |
473 | 473 | #endif |
474 | 474 | } |
... | ... | @@ -500,8 +500,8 @@ uint32_t cpu_ppc_load_decr (CPUState *env) |
500 | 500 | decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); |
501 | 501 | else |
502 | 502 | decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); |
503 | -#if defined(DEBUG_TB) | |
504 | - if (loglevel) { | |
503 | +#if defined(PPC_DEBUG_TB) | |
504 | + if (loglevel != 0) { | |
505 | 505 | fprintf(logfile, "%s: 0x%08x\n", __func__, decr); |
506 | 506 | } |
507 | 507 | #endif |
... | ... | @@ -515,8 +515,8 @@ uint32_t cpu_ppc_load_decr (CPUState *env) |
515 | 515 | static inline void cpu_ppc_decr_excp (CPUState *env) |
516 | 516 | { |
517 | 517 | /* Raise it */ |
518 | -#ifdef DEBUG_TB | |
519 | - if (loglevel) { | |
518 | +#ifdef PPC_DEBUG_TB | |
519 | + if (loglevel != 0) { | |
520 | 520 | fprintf(logfile, "raise decrementer exception\n"); |
521 | 521 | } |
522 | 522 | #endif |
... | ... | @@ -529,8 +529,8 @@ static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, |
529 | 529 | ppc_tb_t *tb_env = env->tb_env; |
530 | 530 | uint64_t now, next; |
531 | 531 | |
532 | -#ifdef DEBUG_TB | |
533 | - if (loglevel) { | |
532 | +#ifdef PPC_DEBUG_TB | |
533 | + if (loglevel != 0) { | |
534 | 534 | fprintf(logfile, "%s: 0x%08x => 0x%08x\n", __func__, decr, value); |
535 | 535 | } |
536 | 536 | #endif |
... | ... | @@ -657,42 +657,69 @@ static void cpu_4xx_fit_cb (void *opaque) |
657 | 657 | if (next == now) |
658 | 658 | next++; |
659 | 659 | qemu_mod_timer(ppcemb_timer->fit_timer, next); |
660 | - tb_env->decr_next = next; | |
661 | 660 | env->spr[SPR_40x_TSR] |= 1 << 26; |
662 | 661 | if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) |
663 | 662 | ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); |
664 | - if (loglevel) { | |
663 | +#ifdef PPC_DEBUG_TB | |
664 | + if (loglevel != 0) { | |
665 | 665 | fprintf(logfile, "%s: ir %d TCR " ADDRX " TSR " ADDRX "\n", __func__, |
666 | 666 | (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), |
667 | 667 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); |
668 | 668 | } |
669 | +#endif | |
669 | 670 | } |
670 | 671 | |
671 | 672 | /* Programmable interval timer */ |
672 | -static void cpu_4xx_pit_cb (void *opaque) | |
673 | +static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) | |
673 | 674 | { |
674 | - CPUState *env; | |
675 | - ppc_tb_t *tb_env; | |
676 | 675 | ppcemb_timer_t *ppcemb_timer; |
677 | 676 | uint64_t now, next; |
678 | 677 | |
679 | - env = opaque; | |
680 | - tb_env = env->tb_env; | |
681 | 678 | ppcemb_timer = tb_env->opaque; |
682 | - now = qemu_get_clock(vm_clock); | |
683 | - if ((env->spr[SPR_40x_TCR] >> 22) & 0x1) { | |
684 | - /* Auto reload */ | |
679 | + if (ppcemb_timer->pit_reload <= 1 || | |
680 | + !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || | |
681 | + (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { | |
682 | + /* Stop PIT */ | |
683 | +#ifdef PPC_DEBUG_TB | |
684 | + if (loglevel != 0) { | |
685 | + fprintf(logfile, "%s: stop PIT\n", __func__); | |
686 | + } | |
687 | +#endif | |
688 | + qemu_del_timer(tb_env->decr_timer); | |
689 | + } else { | |
690 | +#ifdef PPC_DEBUG_TB | |
691 | + if (loglevel != 0) { | |
692 | + fprintf(logfile, "%s: start PIT 0x" REGX "\n", | |
693 | + __func__, ppcemb_timer->pit_reload); | |
694 | + } | |
695 | +#endif | |
696 | + now = qemu_get_clock(vm_clock); | |
685 | 697 | next = now + muldiv64(ppcemb_timer->pit_reload, |
686 | 698 | ticks_per_sec, tb_env->tb_freq); |
699 | + if (is_excp) | |
700 | + next += tb_env->decr_next - now; | |
687 | 701 | if (next == now) |
688 | 702 | next++; |
689 | 703 | qemu_mod_timer(tb_env->decr_timer, next); |
690 | 704 | tb_env->decr_next = next; |
691 | 705 | } |
706 | +} | |
707 | + | |
708 | +static void cpu_4xx_pit_cb (void *opaque) | |
709 | +{ | |
710 | + CPUState *env; | |
711 | + ppc_tb_t *tb_env; | |
712 | + ppcemb_timer_t *ppcemb_timer; | |
713 | + | |
714 | + env = opaque; | |
715 | + tb_env = env->tb_env; | |
716 | + ppcemb_timer = tb_env->opaque; | |
692 | 717 | env->spr[SPR_40x_TSR] |= 1 << 27; |
693 | 718 | if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) |
694 | 719 | ppc_set_irq(env, PPC_INTERRUPT_PIT, 1); |
695 | - if (loglevel) { | |
720 | + start_stop_pit(env, tb_env, 1); | |
721 | +#ifdef PPC_DEBUG_TB | |
722 | + if (loglevel != 0) { | |
696 | 723 | fprintf(logfile, "%s: ar %d ir %d TCR " ADDRX " TSR " ADDRX " " |
697 | 724 | "%016" PRIx64 "\n", __func__, |
698 | 725 | (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), |
... | ... | @@ -700,6 +727,7 @@ static void cpu_4xx_pit_cb (void *opaque) |
700 | 727 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], |
701 | 728 | ppcemb_timer->pit_reload); |
702 | 729 | } |
730 | +#endif | |
703 | 731 | } |
704 | 732 | |
705 | 733 | /* Watchdog timer */ |
... | ... | @@ -734,10 +762,12 @@ static void cpu_4xx_wdt_cb (void *opaque) |
734 | 762 | next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq); |
735 | 763 | if (next == now) |
736 | 764 | next++; |
737 | - if (loglevel) { | |
765 | +#ifdef PPC_DEBUG_TB | |
766 | + if (loglevel != 0) { | |
738 | 767 | fprintf(logfile, "%s: TCR " ADDRX " TSR " ADDRX "\n", __func__, |
739 | 768 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); |
740 | 769 | } |
770 | +#endif | |
741 | 771 | switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { |
742 | 772 | case 0x0: |
743 | 773 | case 0x1: |
... | ... | @@ -776,31 +806,16 @@ void store_40x_pit (CPUState *env, target_ulong val) |
776 | 806 | { |
777 | 807 | ppc_tb_t *tb_env; |
778 | 808 | ppcemb_timer_t *ppcemb_timer; |
779 | - uint64_t now, next; | |
780 | 809 | |
781 | 810 | tb_env = env->tb_env; |
782 | 811 | ppcemb_timer = tb_env->opaque; |
783 | - if (loglevel) { | |
812 | +#ifdef PPC_DEBUG_TB | |
813 | + if (loglevel != 0) { | |
784 | 814 | fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer); |
785 | 815 | } |
816 | +#endif | |
786 | 817 | ppcemb_timer->pit_reload = val; |
787 | - if (val == 0) { | |
788 | - /* Stop PIT */ | |
789 | - if (loglevel) { | |
790 | - fprintf(logfile, "%s: stop PIT\n", __func__); | |
791 | - } | |
792 | - qemu_del_timer(tb_env->decr_timer); | |
793 | - } else { | |
794 | - if (loglevel) { | |
795 | - fprintf(logfile, "%s: start PIT 0x" ADDRX "\n", __func__, val); | |
796 | - } | |
797 | - now = qemu_get_clock(vm_clock); | |
798 | - next = now + muldiv64(val, ticks_per_sec, tb_env->tb_freq); | |
799 | - if (next == now) | |
800 | - next++; | |
801 | - qemu_mod_timer(tb_env->decr_timer, next); | |
802 | - tb_env->decr_next = next; | |
803 | - } | |
818 | + start_stop_pit(env, tb_env, 0); | |
804 | 819 | } |
805 | 820 | |
806 | 821 | target_ulong load_40x_pit (CPUState *env) |
... | ... | @@ -810,30 +825,64 @@ target_ulong load_40x_pit (CPUState *env) |
810 | 825 | |
811 | 826 | void store_booke_tsr (CPUState *env, target_ulong val) |
812 | 827 | { |
813 | - env->spr[SPR_40x_TSR] = val & 0xFC000000; | |
828 | +#ifdef PPC_DEBUG_TB | |
829 | + if (loglevel != 0) { | |
830 | + fprintf(logfile, "%s: val=" ADDRX "\n", __func__, val); | |
831 | + } | |
832 | +#endif | |
833 | + env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000); | |
834 | + if (val & 0x80000000) | |
835 | + ppc_set_irq(env, PPC_INTERRUPT_PIT, 0); | |
814 | 836 | } |
815 | 837 | |
816 | 838 | void store_booke_tcr (CPUState *env, target_ulong val) |
817 | 839 | { |
818 | - env->spr[SPR_40x_TCR] = val & 0xFF800000; | |
840 | + ppc_tb_t *tb_env; | |
841 | + | |
842 | + tb_env = env->tb_env; | |
843 | +#ifdef PPC_DEBUG_TB | |
844 | + if (loglevel != 0) { | |
845 | + fprintf(logfile, "%s: val=" ADDRX "\n", __func__, val); | |
846 | + } | |
847 | +#endif | |
848 | + env->spr[SPR_40x_TCR] = val & 0xFFC00000; | |
849 | + start_stop_pit(env, tb_env, 1); | |
819 | 850 | cpu_4xx_wdt_cb(env); |
820 | 851 | } |
821 | 852 | |
853 | +static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) | |
854 | +{ | |
855 | + CPUState *env = opaque; | |
856 | + ppc_tb_t *tb_env = env->tb_env; | |
857 | + | |
858 | +#ifdef PPC_DEBUG_TB | |
859 | + if (loglevel != 0) { | |
860 | + fprintf(logfile, "%s set new frequency to %u\n", __func__, freq); | |
861 | + } | |
862 | +#endif | |
863 | + tb_env->tb_freq = freq; | |
864 | + /* XXX: we should also update all timers */ | |
865 | +} | |
866 | + | |
822 | 867 | clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq) |
823 | 868 | { |
824 | 869 | ppc_tb_t *tb_env; |
825 | 870 | ppcemb_timer_t *ppcemb_timer; |
826 | 871 | |
827 | 872 | tb_env = qemu_mallocz(sizeof(ppc_tb_t)); |
828 | - if (tb_env == NULL) | |
873 | + if (tb_env == NULL) { | |
829 | 874 | return NULL; |
875 | + } | |
830 | 876 | env->tb_env = tb_env; |
831 | 877 | ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t)); |
832 | 878 | tb_env->tb_freq = freq; |
833 | 879 | tb_env->opaque = ppcemb_timer; |
834 | - if (loglevel) { | |
835 | - fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer); | |
880 | +#ifdef PPC_DEBUG_TB | |
881 | + if (loglevel != 0) { | |
882 | + fprintf(logfile, "%s %p %p %p\n", __func__, tb_env, ppcemb_timer, | |
883 | + &ppc_emb_set_tb_clk); | |
836 | 884 | } |
885 | +#endif | |
837 | 886 | if (ppcemb_timer != NULL) { |
838 | 887 | /* We use decr timer for PIT */ |
839 | 888 | tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env); |
... | ... | @@ -843,8 +892,7 @@ clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq) |
843 | 892 | qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env); |
844 | 893 | } |
845 | 894 | |
846 | - /* XXX: TODO: add callback for clock frequency change */ | |
847 | - return NULL; | |
895 | + return &ppc_emb_set_tb_clk; | |
848 | 896 | } |
849 | 897 | |
850 | 898 | /*****************************************************************************/ | ... | ... |