]> Git Repo - qemu.git/blob - target/mips/cp0_timer.c
target/mips: Clean up handling of CP0 register 17
[qemu.git] / target / mips / cp0_timer.c
1 /*
2  * QEMU MIPS timer support
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22
23 #include "qemu/osdep.h"
24 #include "hw/irq.h"
25 #include "hw/mips/cpudevs.h"
26 #include "qemu/timer.h"
27 #include "sysemu/kvm.h"
28 #include "internal.h"
29
30 #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
31
32 /* XXX: do not use a global */
33 uint32_t cpu_mips_get_random(CPUMIPSState *env)
34 {
35     static uint32_t seed = 1;
36     static uint32_t prev_idx = 0;
37     uint32_t idx;
38     uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
39
40     if (nb_rand_tlb == 1) {
41         return env->tlb->nb_tlb - 1;
42     }
43
44     /* Don't return same value twice, so get another value */
45     do {
46         /*
47          * Use a simple algorithm of Linear Congruential Generator
48          * from ISO/IEC 9899 standard.
49          */
50         seed = 1103515245 * seed + 12345;
51         idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
52     } while (idx == prev_idx);
53     prev_idx = idx;
54     return idx;
55 }
56
57 /* MIPS R4K timer */
58 static void cpu_mips_timer_update(CPUMIPSState *env)
59 {
60     uint64_t now, next;
61     uint32_t wait;
62
63     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
64     wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD);
65     next = now + (uint64_t)wait * TIMER_PERIOD;
66     timer_mod(env->timer, next);
67 }
68
69 /* Expire the timer.  */
70 static void cpu_mips_timer_expire(CPUMIPSState *env)
71 {
72     cpu_mips_timer_update(env);
73     if (env->insn_flags & ISA_MIPS32R2) {
74         env->CP0_Cause |= 1 << CP0Ca_TI;
75     }
76     qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
77 }
78
79 uint32_t cpu_mips_get_count(CPUMIPSState *env)
80 {
81     if (env->CP0_Cause & (1 << CP0Ca_DC)) {
82         return env->CP0_Count;
83     } else {
84         uint64_t now;
85
86         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
87         if (timer_pending(env->timer)
88             && timer_expired(env->timer, now)) {
89             /* The timer has already expired.  */
90             cpu_mips_timer_expire(env);
91         }
92
93         return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD);
94     }
95 }
96
97 void cpu_mips_store_count(CPUMIPSState *env, uint32_t count)
98 {
99     /*
100      * This gets called from cpu_state_reset(), potentially before timer init.
101      * So env->timer may be NULL, which is also the case with KVM enabled so
102      * treat timer as disabled in that case.
103      */
104     if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer) {
105         env->CP0_Count = count;
106     } else {
107         /* Store new count register */
108         env->CP0_Count = count -
109                (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
110         /* Update timer timer */
111         cpu_mips_timer_update(env);
112     }
113 }
114
115 void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value)
116 {
117     env->CP0_Compare = value;
118     if (!(env->CP0_Cause & (1 << CP0Ca_DC))) {
119         cpu_mips_timer_update(env);
120     }
121     if (env->insn_flags & ISA_MIPS32R2) {
122         env->CP0_Cause &= ~(1 << CP0Ca_TI);
123     }
124     qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
125 }
126
127 void cpu_mips_start_count(CPUMIPSState *env)
128 {
129     cpu_mips_store_count(env, env->CP0_Count);
130 }
131
132 void cpu_mips_stop_count(CPUMIPSState *env)
133 {
134     /* Store the current value */
135     env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
136                                  TIMER_PERIOD);
137 }
138
139 static void mips_timer_cb(void *opaque)
140 {
141     CPUMIPSState *env;
142
143     env = opaque;
144
145     if (env->CP0_Cause & (1 << CP0Ca_DC)) {
146         return;
147     }
148
149     /*
150      * ??? This callback should occur when the counter is exactly equal to
151      * the comparator value.  Offset the count by one to avoid immediately
152      * retriggering the callback before any virtual time has passed.
153      */
154     env->CP0_Count++;
155     cpu_mips_timer_expire(env);
156     env->CP0_Count--;
157 }
158
159 void cpu_mips_clock_init(MIPSCPU *cpu)
160 {
161     CPUMIPSState *env = &cpu->env;
162
163     /*
164      * If we're in KVM mode, don't create the periodic timer, that is handled in
165      * kernel.
166      */
167     if (!kvm_enabled()) {
168         env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env);
169     }
170 }
This page took 0.03248 seconds and 4 git commands to generate.