Revision dbdd2506 hw/ppc.c
b/hw/ppc.c | ||
---|---|---|
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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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) { |
Also available in: Unified diff