1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include "hw/hw.h"
24#include "hw/mips/cpudevs.h"
25#include "qemu/timer.h"
26
27#define TIMER_FREQ 100 * 1000 * 1000
28
29
30uint32_t cpu_mips_get_random (CPUMIPSState *env)
31{
32 static uint32_t lfsr = 1;
33 static uint32_t prev_idx = 0;
34 uint32_t idx;
35
36 do {
37 lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
38 idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
39 } while (idx == prev_idx);
40 prev_idx = idx;
41 return idx;
42}
43
44
45static void cpu_mips_timer_update(CPUMIPSState *env)
46{
47 uint64_t now, next;
48 uint32_t wait;
49
50 now = qemu_get_clock_ns(vm_clock);
51 wait = env->CP0_Compare - env->CP0_Count -
52 (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
53 next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
54 qemu_mod_timer(env->timer, next);
55}
56
57
58static void cpu_mips_timer_expire(CPUMIPSState *env)
59{
60 cpu_mips_timer_update(env);
61 if (env->insn_flags & ISA_MIPS32R2) {
62 env->CP0_Cause |= 1 << CP0Ca_TI;
63 }
64 qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
65}
66
67uint32_t cpu_mips_get_count (CPUMIPSState *env)
68{
69 if (env->CP0_Cause & (1 << CP0Ca_DC)) {
70 return env->CP0_Count;
71 } else {
72 uint64_t now;
73
74 now = qemu_get_clock_ns(vm_clock);
75 if (qemu_timer_pending(env->timer)
76 && qemu_timer_expired(env->timer, now)) {
77
78 cpu_mips_timer_expire(env);
79 }
80
81 return env->CP0_Count +
82 (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
83 }
84}
85
86void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
87{
88 if (env->CP0_Cause & (1 << CP0Ca_DC))
89 env->CP0_Count = count;
90 else {
91
92 env->CP0_Count =
93 count - (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock),
94 TIMER_FREQ, get_ticks_per_sec());
95
96 cpu_mips_timer_update(env);
97 }
98}
99
100void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
101{
102 env->CP0_Compare = value;
103 if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
104 cpu_mips_timer_update(env);
105 if (env->insn_flags & ISA_MIPS32R2)
106 env->CP0_Cause &= ~(1 << CP0Ca_TI);
107 qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
108}
109
110void cpu_mips_start_count(CPUMIPSState *env)
111{
112 cpu_mips_store_count(env, env->CP0_Count);
113}
114
115void cpu_mips_stop_count(CPUMIPSState *env)
116{
117
118 env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock),
119 TIMER_FREQ, get_ticks_per_sec());
120}
121
122static void mips_timer_cb (void *opaque)
123{
124 CPUMIPSState *env;
125
126 env = opaque;
127#if 0
128 qemu_log("%s\n", __func__);
129#endif
130
131 if (env->CP0_Cause & (1 << CP0Ca_DC))
132 return;
133
134
135
136
137 env->CP0_Count++;
138 cpu_mips_timer_expire(env);
139 env->CP0_Count--;
140}
141
142void cpu_mips_clock_init (CPUMIPSState *env)
143{
144 env->timer = qemu_new_timer_ns(vm_clock, &mips_timer_cb, env);
145 env->CP0_Compare = 0;
146 cpu_mips_store_count(env, 1);
147}
148