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