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