]>
Commit | Line | Data |
---|---|---|
2328826b MF |
1 | /* |
2 | * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * * Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * * Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * * Neither the name of the Open Source and Linux Lab nor the | |
13 | * names of its contributors may be used to endorse or promote products | |
14 | * derived from this software without specific prior written permission. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 | */ | |
27 | ||
83c9f4ca | 28 | #include "hw/hw.h" |
1de7afc9 PB |
29 | #include "qemu/log.h" |
30 | #include "qemu/timer.h" | |
2328826b | 31 | |
5bfcb36e | 32 | void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) |
b994e91b | 33 | { |
c9e9521f | 34 | uint32_t old_ccount = env->sregs[CCOUNT] + 1; |
b994e91b MF |
35 | |
36 | env->sregs[CCOUNT] += d; | |
37 | ||
38 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { | |
39 | int i; | |
40 | for (i = 0; i < env->config->nccompare; ++i) { | |
c9e9521f | 41 | if (env->sregs[CCOMPARE + i] - old_ccount < d) { |
b994e91b MF |
42 | xtensa_timer_irq(env, i, 1); |
43 | } | |
44 | } | |
45 | } | |
46 | } | |
47 | ||
5bfcb36e | 48 | void check_interrupts(CPUXtensaState *env) |
b994e91b | 49 | { |
259186a7 | 50 | CPUState *cs = CPU(xtensa_env_get_cpu(env)); |
b994e91b MF |
51 | int minlevel = xtensa_get_cintlevel(env); |
52 | uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; | |
53 | int level; | |
54 | ||
bc72ad67 | 55 | /* If the CPU is halted advance CCOUNT according to the QEMU_CLOCK_VIRTUAL time |
b994e91b MF |
56 | * elapsed since the moment when it was advanced last time. |
57 | */ | |
259186a7 | 58 | if (cs->halted) { |
bc72ad67 | 59 | int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
b994e91b MF |
60 | |
61 | xtensa_advance_ccount(env, | |
62 | muldiv64(now - env->halt_clock, | |
63 | env->config->clock_freq_khz, 1000000)); | |
64 | env->halt_clock = now; | |
65 | } | |
66 | for (level = env->config->nlevel; level > minlevel; --level) { | |
67 | if (env->config->level_mask[level] & int_set_enabled) { | |
68 | env->pending_irq_level = level; | |
c3affe56 | 69 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
b994e91b MF |
70 | qemu_log_mask(CPU_LOG_INT, |
71 | "%s level = %d, cintlevel = %d, " | |
72 | "pc = %08x, a0 = %08x, ps = %08x, " | |
73 | "intset = %08x, intenable = %08x, " | |
74 | "ccount = %08x\n", | |
75 | __func__, level, xtensa_get_cintlevel(env), | |
76 | env->pc, env->regs[0], env->sregs[PS], | |
77 | env->sregs[INTSET], env->sregs[INTENABLE], | |
78 | env->sregs[CCOUNT]); | |
79 | return; | |
80 | } | |
81 | } | |
82 | env->pending_irq_level = 0; | |
d8ed887b | 83 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); |
b994e91b MF |
84 | } |
85 | ||
86 | static void xtensa_set_irq(void *opaque, int irq, int active) | |
87 | { | |
5bfcb36e | 88 | CPUXtensaState *env = opaque; |
b994e91b MF |
89 | |
90 | if (irq >= env->config->ninterrupt) { | |
91 | qemu_log("%s: bad IRQ %d\n", __func__, irq); | |
92 | } else { | |
93 | uint32_t irq_bit = 1 << irq; | |
94 | ||
95 | if (active) { | |
96 | env->sregs[INTSET] |= irq_bit; | |
97 | } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { | |
98 | env->sregs[INTSET] &= ~irq_bit; | |
99 | } | |
100 | ||
101 | check_interrupts(env); | |
102 | } | |
103 | } | |
104 | ||
5bfcb36e | 105 | void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active) |
b994e91b MF |
106 | { |
107 | qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); | |
108 | } | |
109 | ||
5bfcb36e | 110 | void xtensa_rearm_ccompare_timer(CPUXtensaState *env) |
890c6333 MF |
111 | { |
112 | int i; | |
113 | uint32_t wake_ccount = env->sregs[CCOUNT] - 1; | |
114 | ||
115 | for (i = 0; i < env->config->nccompare; ++i) { | |
116 | if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] < | |
117 | wake_ccount - env->sregs[CCOUNT]) { | |
118 | wake_ccount = env->sregs[CCOMPARE + i]; | |
119 | } | |
120 | } | |
121 | env->wake_ccount = wake_ccount; | |
bc72ad67 | 122 | timer_mod(env->ccompare_timer, env->halt_clock + |
890c6333 MF |
123 | muldiv64(wake_ccount - env->sregs[CCOUNT], |
124 | 1000000, env->config->clock_freq_khz)); | |
125 | } | |
126 | ||
b994e91b MF |
127 | static void xtensa_ccompare_cb(void *opaque) |
128 | { | |
79bbf20b AF |
129 | XtensaCPU *cpu = opaque; |
130 | CPUXtensaState *env = &cpu->env; | |
259186a7 | 131 | CPUState *cs = CPU(cpu); |
890c6333 | 132 | |
259186a7 | 133 | if (cs->halted) { |
bc72ad67 | 134 | env->halt_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
890c6333 | 135 | xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); |
259186a7 | 136 | if (!cpu_has_work(cs)) { |
890c6333 MF |
137 | env->sregs[CCOUNT] = env->wake_ccount + 1; |
138 | xtensa_rearm_ccompare_timer(env); | |
139 | } | |
140 | } | |
b994e91b MF |
141 | } |
142 | ||
5bfcb36e | 143 | void xtensa_irq_init(CPUXtensaState *env) |
b994e91b | 144 | { |
79bbf20b AF |
145 | XtensaCPU *cpu = xtensa_env_get_cpu(env); |
146 | ||
b994e91b MF |
147 | env->irq_inputs = (void **)qemu_allocate_irqs( |
148 | xtensa_set_irq, env, env->config->ninterrupt); | |
149 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) && | |
150 | env->config->nccompare > 0) { | |
151 | env->ccompare_timer = | |
bc72ad67 | 152 | timer_new_ns(QEMU_CLOCK_VIRTUAL, &xtensa_ccompare_cb, cpu); |
b994e91b MF |
153 | } |
154 | } | |
b8929a54 | 155 | |
5bfcb36e | 156 | void *xtensa_get_extint(CPUXtensaState *env, unsigned extint) |
b8929a54 MF |
157 | { |
158 | if (extint < env->config->nextint) { | |
159 | unsigned irq = env->config->extint[extint]; | |
160 | return env->irq_inputs[irq]; | |
161 | } else { | |
162 | qemu_log("%s: trying to acquire invalid external interrupt %d\n", | |
163 | __func__, extint); | |
164 | return NULL; | |
165 | } | |
166 | } |