Statistics
| Branch: | Revision:

root / hw / mips_timer.c @ 6059631c

History | View | Annotate | Download (4.4 kB)

1 7b9cbadb Aurelien Jarno
/*
2 7b9cbadb Aurelien Jarno
 * QEMU MIPS timer support
3 7b9cbadb Aurelien Jarno
 *
4 7b9cbadb Aurelien Jarno
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 7b9cbadb Aurelien Jarno
 * of this software and associated documentation files (the "Software"), to deal
6 7b9cbadb Aurelien Jarno
 * in the Software without restriction, including without limitation the rights
7 7b9cbadb Aurelien Jarno
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 7b9cbadb Aurelien Jarno
 * copies of the Software, and to permit persons to whom the Software is
9 7b9cbadb Aurelien Jarno
 * furnished to do so, subject to the following conditions:
10 7b9cbadb Aurelien Jarno
 *
11 7b9cbadb Aurelien Jarno
 * The above copyright notice and this permission notice shall be included in
12 7b9cbadb Aurelien Jarno
 * all copies or substantial portions of the Software.
13 7b9cbadb Aurelien Jarno
 *
14 7b9cbadb Aurelien Jarno
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 7b9cbadb Aurelien Jarno
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 7b9cbadb Aurelien Jarno
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 7b9cbadb Aurelien Jarno
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 7b9cbadb Aurelien Jarno
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 7b9cbadb Aurelien Jarno
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 7b9cbadb Aurelien Jarno
 * THE SOFTWARE.
21 7b9cbadb Aurelien Jarno
 */
22 7b9cbadb Aurelien Jarno
23 87ecb68b pbrook
#include "hw.h"
24 b970ea8f Blue Swirl
#include "mips_cpudevs.h"
25 87ecb68b pbrook
#include "qemu-timer.h"
26 e16fe40c ths
27 ea86e4e6 aurel32
#define TIMER_FREQ        100 * 1000 * 1000
28 ea86e4e6 aurel32
29 e16fe40c ths
/* XXX: do not use a global */
30 e16fe40c ths
uint32_t cpu_mips_get_random (CPUState *env)
31 e16fe40c ths
{
32 59d94130 aurel32
    static uint32_t lfsr = 1;
33 59d94130 aurel32
    static uint32_t prev_idx = 0;
34 e16fe40c ths
    uint32_t idx;
35 59d94130 aurel32
    /* Don't return same value twice, so get another value */
36 59d94130 aurel32
    do {
37 59d94130 aurel32
        lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
38 59d94130 aurel32
        idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
39 59d94130 aurel32
    } while (idx == prev_idx);
40 59d94130 aurel32
    prev_idx = idx;
41 e16fe40c ths
    return idx;
42 e16fe40c ths
}
43 e16fe40c ths
44 e16fe40c ths
/* MIPS R4K timer */
45 ea86e4e6 aurel32
static void cpu_mips_timer_update(CPUState *env)
46 e16fe40c ths
{
47 e16fe40c ths
    uint64_t now, next;
48 ea86e4e6 aurel32
    uint32_t wait;
49 39d51eb8 ths
50 e16fe40c ths
    now = qemu_get_clock(vm_clock);
51 ea86e4e6 aurel32
    wait = env->CP0_Compare - env->CP0_Count -
52 6ee093c9 Juan Quintela
            (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
53 6ee093c9 Juan Quintela
    next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
54 e16fe40c ths
    qemu_mod_timer(env->timer, next);
55 e16fe40c ths
}
56 e16fe40c ths
57 b1dfe643 Edgar E. Iglesias
/* Expire the timer.  */
58 b1dfe643 Edgar E. Iglesias
static void cpu_mips_timer_expire(CPUState *env)
59 b1dfe643 Edgar E. Iglesias
{
60 b1dfe643 Edgar E. Iglesias
    cpu_mips_timer_update(env);
61 b1dfe643 Edgar E. Iglesias
    if (env->insn_flags & ISA_MIPS32R2) {
62 b1dfe643 Edgar E. Iglesias
        env->CP0_Cause |= 1 << CP0Ca_TI;
63 b1dfe643 Edgar E. Iglesias
    }
64 b1dfe643 Edgar E. Iglesias
    qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
65 b1dfe643 Edgar E. Iglesias
}
66 b1dfe643 Edgar E. Iglesias
67 b1dfe643 Edgar E. Iglesias
uint32_t cpu_mips_get_count (CPUState *env)
68 b1dfe643 Edgar E. Iglesias
{
69 b1dfe643 Edgar E. Iglesias
    if (env->CP0_Cause & (1 << CP0Ca_DC)) {
70 b1dfe643 Edgar E. Iglesias
        return env->CP0_Count;
71 b1dfe643 Edgar E. Iglesias
    } else {
72 e027e1f0 Edgar E. Iglesias
        uint64_t now;
73 e027e1f0 Edgar E. Iglesias
74 e027e1f0 Edgar E. Iglesias
        now = qemu_get_clock(vm_clock);
75 e027e1f0 Edgar E. Iglesias
        if (qemu_timer_pending(env->timer)
76 e027e1f0 Edgar E. Iglesias
            && qemu_timer_expired(env->timer, now)) {
77 e027e1f0 Edgar E. Iglesias
            /* The timer has already expired.  */
78 e027e1f0 Edgar E. Iglesias
            cpu_mips_timer_expire(env);
79 e027e1f0 Edgar E. Iglesias
        }
80 e027e1f0 Edgar E. Iglesias
81 b1dfe643 Edgar E. Iglesias
        return env->CP0_Count +
82 e027e1f0 Edgar E. Iglesias
            (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
83 b1dfe643 Edgar E. Iglesias
    }
84 b1dfe643 Edgar E. Iglesias
}
85 b1dfe643 Edgar E. Iglesias
86 ea86e4e6 aurel32
void cpu_mips_store_count (CPUState *env, uint32_t count)
87 e16fe40c ths
{
88 3529b538 ths
    if (env->CP0_Cause & (1 << CP0Ca_DC))
89 ea86e4e6 aurel32
        env->CP0_Count = count;
90 ea86e4e6 aurel32
    else {
91 ea86e4e6 aurel32
        /* Store new count register */
92 ea86e4e6 aurel32
        env->CP0_Count =
93 ea86e4e6 aurel32
            count - (uint32_t)muldiv64(qemu_get_clock(vm_clock),
94 6ee093c9 Juan Quintela
                                       TIMER_FREQ, get_ticks_per_sec());
95 ea86e4e6 aurel32
        /* Update timer timer */
96 ea86e4e6 aurel32
        cpu_mips_timer_update(env);
97 ea86e4e6 aurel32
    }
98 e16fe40c ths
}
99 e16fe40c ths
100 e16fe40c ths
void cpu_mips_store_compare (CPUState *env, uint32_t value)
101 e16fe40c ths
{
102 3529b538 ths
    env->CP0_Compare = value;
103 ea86e4e6 aurel32
    if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
104 ea86e4e6 aurel32
        cpu_mips_timer_update(env);
105 ea86e4e6 aurel32
    if (env->insn_flags & ISA_MIPS32R2)
106 39d51eb8 ths
        env->CP0_Cause &= ~(1 << CP0Ca_TI);
107 42532189 ths
    qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
108 42532189 ths
}
109 42532189 ths
110 42532189 ths
void cpu_mips_start_count(CPUState *env)
111 42532189 ths
{
112 42532189 ths
    cpu_mips_store_count(env, env->CP0_Count);
113 42532189 ths
}
114 42532189 ths
115 42532189 ths
void cpu_mips_stop_count(CPUState *env)
116 42532189 ths
{
117 42532189 ths
    /* Store the current value */
118 42532189 ths
    env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock(vm_clock),
119 6ee093c9 Juan Quintela
                                         TIMER_FREQ, get_ticks_per_sec());
120 e16fe40c ths
}
121 e16fe40c ths
122 e16fe40c ths
static void mips_timer_cb (void *opaque)
123 e16fe40c ths
{
124 e16fe40c ths
    CPUState *env;
125 e16fe40c ths
126 e16fe40c ths
    env = opaque;
127 e16fe40c ths
#if 0
128 93fcfe39 aliguori
    qemu_log("%s\n", __func__);
129 e16fe40c ths
#endif
130 42532189 ths
131 42532189 ths
    if (env->CP0_Cause & (1 << CP0Ca_DC))
132 42532189 ths
        return;
133 42532189 ths
134 2e70f6ef pbrook
    /* ??? This callback should occur when the counter is exactly equal to
135 2e70f6ef pbrook
       the comparator value.  Offset the count by one to avoid immediately
136 2e70f6ef pbrook
       retriggering the callback before any virtual time has passed.  */
137 2e70f6ef pbrook
    env->CP0_Count++;
138 b1dfe643 Edgar E. Iglesias
    cpu_mips_timer_expire(env);
139 2e70f6ef pbrook
    env->CP0_Count--;
140 e16fe40c ths
}
141 e16fe40c ths
142 e16fe40c ths
void cpu_mips_clock_init (CPUState *env)
143 e16fe40c ths
{
144 e16fe40c ths
    env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env);
145 e16fe40c ths
    env->CP0_Compare = 0;
146 ea86e4e6 aurel32
    cpu_mips_store_count(env, 1);
147 e16fe40c ths
}