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