]> git.proxmox.com Git - mirror_qemu.git/blame - hw/mips/cputimer.c
Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging
[mirror_qemu.git] / hw / mips / cputimer.c
CommitLineData
7b9cbadb
AJ
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
83c9f4ca 23#include "hw/hw.h"
0d09e41a 24#include "hw/mips/cpudevs.h"
1de7afc9 25#include "qemu/timer.h"
353a243e 26#include "sysemu/kvm.h"
e16fe40c 27
683dca6b 28#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
ea86e4e6 29
e16fe40c 30/* XXX: do not use a global */
61c56c8c 31uint32_t cpu_mips_get_random (CPUMIPSState *env)
e16fe40c 32{
ceb0ee14 33 static uint32_t seed = 1;
59d94130 34 static uint32_t prev_idx = 0;
e16fe40c 35 uint32_t idx;
3adafef2
LA
36 uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
37
38 if (nb_rand_tlb == 1) {
39 return env->tlb->nb_tlb - 1;
40 }
41
59d94130
AJ
42 /* Don't return same value twice, so get another value */
43 do {
ceb0ee14
SV
44 /* Use a simple algorithm of Linear Congruential Generator
45 * from ISO/IEC 9899 standard. */
46 seed = 1103515245 * seed + 12345;
3adafef2 47 idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
59d94130
AJ
48 } while (idx == prev_idx);
49 prev_idx = idx;
e16fe40c
TS
50 return idx;
51}
52
53/* MIPS R4K timer */
61c56c8c 54static void cpu_mips_timer_update(CPUMIPSState *env)
e16fe40c
TS
55{
56 uint64_t now, next;
ea86e4e6 57 uint32_t wait;
39d51eb8 58
bc72ad67 59 now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
683dca6b
LV
60 wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD);
61 next = now + (uint64_t)wait * TIMER_PERIOD;
bc72ad67 62 timer_mod(env->timer, next);
e16fe40c
TS
63}
64
b1dfe643 65/* Expire the timer. */
61c56c8c 66static void cpu_mips_timer_expire(CPUMIPSState *env)
b1dfe643
EI
67{
68 cpu_mips_timer_update(env);
69 if (env->insn_flags & ISA_MIPS32R2) {
70 env->CP0_Cause |= 1 << CP0Ca_TI;
71 }
72 qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
73}
74
61c56c8c 75uint32_t cpu_mips_get_count (CPUMIPSState *env)
b1dfe643
EI
76{
77 if (env->CP0_Cause & (1 << CP0Ca_DC)) {
78 return env->CP0_Count;
79 } else {
e027e1f0
EI
80 uint64_t now;
81
bc72ad67 82 now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
e93379b0
AB
83 if (timer_pending(env->timer)
84 && timer_expired(env->timer, now)) {
e027e1f0
EI
85 /* The timer has already expired. */
86 cpu_mips_timer_expire(env);
87 }
88
683dca6b 89 return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD);
b1dfe643
EI
90 }
91}
92
61c56c8c 93void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
e16fe40c 94{
4b69c7e2
JH
95 /*
96 * This gets called from cpu_state_reset(), potentially before timer init.
97 * So env->timer may be NULL, which is also the case with KVM enabled so
98 * treat timer as disabled in that case.
99 */
100 if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer)
ea86e4e6
AJ
101 env->CP0_Count = count;
102 else {
103 /* Store new count register */
683dca6b
LV
104 env->CP0_Count = count -
105 (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
ea86e4e6
AJ
106 /* Update timer timer */
107 cpu_mips_timer_update(env);
108 }
e16fe40c
TS
109}
110
61c56c8c 111void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
e16fe40c 112{
3529b538 113 env->CP0_Compare = value;
ea86e4e6
AJ
114 if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
115 cpu_mips_timer_update(env);
116 if (env->insn_flags & ISA_MIPS32R2)
39d51eb8 117 env->CP0_Cause &= ~(1 << CP0Ca_TI);
42532189
TS
118 qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
119}
120
61c56c8c 121void cpu_mips_start_count(CPUMIPSState *env)
42532189
TS
122{
123 cpu_mips_store_count(env, env->CP0_Count);
124}
125
61c56c8c 126void cpu_mips_stop_count(CPUMIPSState *env)
42532189
TS
127{
128 /* Store the current value */
683dca6b
LV
129 env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
130 TIMER_PERIOD);
e16fe40c
TS
131}
132
133static void mips_timer_cb (void *opaque)
134{
61c56c8c 135 CPUMIPSState *env;
e16fe40c
TS
136
137 env = opaque;
138#if 0
93fcfe39 139 qemu_log("%s\n", __func__);
e16fe40c 140#endif
42532189
TS
141
142 if (env->CP0_Cause & (1 << CP0Ca_DC))
143 return;
144
2e70f6ef
PB
145 /* ??? This callback should occur when the counter is exactly equal to
146 the comparator value. Offset the count by one to avoid immediately
147 retriggering the callback before any virtual time has passed. */
148 env->CP0_Count++;
b1dfe643 149 cpu_mips_timer_expire(env);
2e70f6ef 150 env->CP0_Count--;
e16fe40c
TS
151}
152
61c56c8c 153void cpu_mips_clock_init (CPUMIPSState *env)
e16fe40c 154{
353a243e
SL
155 /*
156 * If we're in KVM mode, don't create the periodic timer, that is handled in
157 * kernel.
158 */
159 if (!kvm_enabled()) {
160 env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env);
161 }
e16fe40c 162}