root / hw / xtensa_pic.c @ 0200db65
History | View | Annotate | Download (5.7 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 | 2328826b | Max Filippov | #include "pc.h" |
30 | b994e91b | Max Filippov | #include "qemu-log.h" |
31 | b994e91b | Max Filippov | #include "qemu-timer.h" |
32 | 2328826b | Max Filippov | |
33 | 2328826b | Max Filippov | /* Stub functions for hardware that doesn't exist. */
|
34 | 2328826b | Max Filippov | void pic_info(Monitor *mon)
|
35 | 2328826b | Max Filippov | { |
36 | 2328826b | Max Filippov | } |
37 | 2328826b | Max Filippov | |
38 | 2328826b | Max Filippov | void irq_info(Monitor *mon)
|
39 | 2328826b | Max Filippov | { |
40 | 2328826b | Max Filippov | } |
41 | b994e91b | Max Filippov | |
42 | b994e91b | Max Filippov | void xtensa_advance_ccount(CPUState *env, uint32_t d)
|
43 | b994e91b | Max Filippov | { |
44 | b994e91b | Max Filippov | uint32_t old_ccount = env->sregs[CCOUNT]; |
45 | b994e91b | Max Filippov | |
46 | b994e91b | Max Filippov | env->sregs[CCOUNT] += d; |
47 | b994e91b | Max Filippov | |
48 | b994e91b | Max Filippov | if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
|
49 | b994e91b | Max Filippov | int i;
|
50 | b994e91b | Max Filippov | for (i = 0; i < env->config->nccompare; ++i) { |
51 | b994e91b | Max Filippov | if (env->sregs[CCOMPARE + i] - old_ccount <= d) {
|
52 | b994e91b | Max Filippov | xtensa_timer_irq(env, i, 1);
|
53 | b994e91b | Max Filippov | } |
54 | b994e91b | Max Filippov | } |
55 | b994e91b | Max Filippov | } |
56 | b994e91b | Max Filippov | } |
57 | b994e91b | Max Filippov | |
58 | b994e91b | Max Filippov | void check_interrupts(CPUState *env)
|
59 | b994e91b | Max Filippov | { |
60 | b994e91b | Max Filippov | int minlevel = xtensa_get_cintlevel(env);
|
61 | b994e91b | Max Filippov | uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; |
62 | b994e91b | Max Filippov | int level;
|
63 | b994e91b | Max Filippov | |
64 | b994e91b | Max Filippov | /* If the CPU is halted advance CCOUNT according to the vm_clock time
|
65 | b994e91b | Max Filippov | * elapsed since the moment when it was advanced last time.
|
66 | b994e91b | Max Filippov | */
|
67 | b994e91b | Max Filippov | if (env->halted) {
|
68 | b994e91b | Max Filippov | int64_t now = qemu_get_clock_ns(vm_clock); |
69 | b994e91b | Max Filippov | |
70 | b994e91b | Max Filippov | xtensa_advance_ccount(env, |
71 | b994e91b | Max Filippov | muldiv64(now - env->halt_clock, |
72 | b994e91b | Max Filippov | env->config->clock_freq_khz, 1000000));
|
73 | b994e91b | Max Filippov | env->halt_clock = now; |
74 | b994e91b | Max Filippov | } |
75 | b994e91b | Max Filippov | for (level = env->config->nlevel; level > minlevel; --level) {
|
76 | b994e91b | Max Filippov | if (env->config->level_mask[level] & int_set_enabled) {
|
77 | b994e91b | Max Filippov | env->pending_irq_level = level; |
78 | b994e91b | Max Filippov | cpu_interrupt(env, CPU_INTERRUPT_HARD); |
79 | b994e91b | Max Filippov | qemu_log_mask(CPU_LOG_INT, |
80 | b994e91b | Max Filippov | "%s level = %d, cintlevel = %d, "
|
81 | b994e91b | Max Filippov | "pc = %08x, a0 = %08x, ps = %08x, "
|
82 | b994e91b | Max Filippov | "intset = %08x, intenable = %08x, "
|
83 | b994e91b | Max Filippov | "ccount = %08x\n",
|
84 | b994e91b | Max Filippov | __func__, level, xtensa_get_cintlevel(env), |
85 | b994e91b | Max Filippov | env->pc, env->regs[0], env->sregs[PS],
|
86 | b994e91b | Max Filippov | env->sregs[INTSET], env->sregs[INTENABLE], |
87 | b994e91b | Max Filippov | env->sregs[CCOUNT]); |
88 | b994e91b | Max Filippov | return;
|
89 | b994e91b | Max Filippov | } |
90 | b994e91b | Max Filippov | } |
91 | b994e91b | Max Filippov | env->pending_irq_level = 0;
|
92 | b994e91b | Max Filippov | cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); |
93 | b994e91b | Max Filippov | } |
94 | b994e91b | Max Filippov | |
95 | b994e91b | Max Filippov | static void xtensa_set_irq(void *opaque, int irq, int active) |
96 | b994e91b | Max Filippov | { |
97 | b994e91b | Max Filippov | CPUState *env = opaque; |
98 | b994e91b | Max Filippov | |
99 | b994e91b | Max Filippov | if (irq >= env->config->ninterrupt) {
|
100 | b994e91b | Max Filippov | qemu_log("%s: bad IRQ %d\n", __func__, irq);
|
101 | b994e91b | Max Filippov | } else {
|
102 | b994e91b | Max Filippov | uint32_t irq_bit = 1 << irq;
|
103 | b994e91b | Max Filippov | |
104 | b994e91b | Max Filippov | if (active) {
|
105 | b994e91b | Max Filippov | env->sregs[INTSET] |= irq_bit; |
106 | b994e91b | Max Filippov | } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { |
107 | b994e91b | Max Filippov | env->sregs[INTSET] &= ~irq_bit; |
108 | b994e91b | Max Filippov | } |
109 | b994e91b | Max Filippov | |
110 | b994e91b | Max Filippov | check_interrupts(env); |
111 | b994e91b | Max Filippov | } |
112 | b994e91b | Max Filippov | } |
113 | b994e91b | Max Filippov | |
114 | b994e91b | Max Filippov | void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active)
|
115 | b994e91b | Max Filippov | { |
116 | b994e91b | Max Filippov | qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); |
117 | b994e91b | Max Filippov | } |
118 | b994e91b | Max Filippov | |
119 | 890c6333 | Max Filippov | void xtensa_rearm_ccompare_timer(CPUState *env)
|
120 | 890c6333 | Max Filippov | { |
121 | 890c6333 | Max Filippov | int i;
|
122 | 890c6333 | Max Filippov | uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
|
123 | 890c6333 | Max Filippov | |
124 | 890c6333 | Max Filippov | for (i = 0; i < env->config->nccompare; ++i) { |
125 | 890c6333 | Max Filippov | if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
|
126 | 890c6333 | Max Filippov | wake_ccount - env->sregs[CCOUNT]) { |
127 | 890c6333 | Max Filippov | wake_ccount = env->sregs[CCOMPARE + i]; |
128 | 890c6333 | Max Filippov | } |
129 | 890c6333 | Max Filippov | } |
130 | 890c6333 | Max Filippov | env->wake_ccount = wake_ccount; |
131 | 890c6333 | Max Filippov | qemu_mod_timer(env->ccompare_timer, env->halt_clock + |
132 | 890c6333 | Max Filippov | muldiv64(wake_ccount - env->sregs[CCOUNT], |
133 | 890c6333 | Max Filippov | 1000000, env->config->clock_freq_khz));
|
134 | 890c6333 | Max Filippov | } |
135 | 890c6333 | Max Filippov | |
136 | b994e91b | Max Filippov | static void xtensa_ccompare_cb(void *opaque) |
137 | b994e91b | Max Filippov | { |
138 | b994e91b | Max Filippov | CPUState *env = opaque; |
139 | 890c6333 | Max Filippov | |
140 | 890c6333 | Max Filippov | if (env->halted) {
|
141 | 890c6333 | Max Filippov | env->halt_clock = qemu_get_clock_ns(vm_clock); |
142 | 890c6333 | Max Filippov | xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); |
143 | 890c6333 | Max Filippov | if (!cpu_has_work(env)) {
|
144 | 890c6333 | Max Filippov | env->sregs[CCOUNT] = env->wake_ccount + 1;
|
145 | 890c6333 | Max Filippov | xtensa_rearm_ccompare_timer(env); |
146 | 890c6333 | Max Filippov | } |
147 | 890c6333 | Max Filippov | } |
148 | b994e91b | Max Filippov | } |
149 | b994e91b | Max Filippov | |
150 | b994e91b | Max Filippov | void xtensa_irq_init(CPUState *env)
|
151 | b994e91b | Max Filippov | { |
152 | b994e91b | Max Filippov | env->irq_inputs = (void **)qemu_allocate_irqs(
|
153 | b994e91b | Max Filippov | xtensa_set_irq, env, env->config->ninterrupt); |
154 | b994e91b | Max Filippov | if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) &&
|
155 | b994e91b | Max Filippov | env->config->nccompare > 0) {
|
156 | b994e91b | Max Filippov | env->ccompare_timer = |
157 | b994e91b | Max Filippov | qemu_new_timer_ns(vm_clock, &xtensa_ccompare_cb, env); |
158 | b994e91b | Max Filippov | } |
159 | b994e91b | Max Filippov | } |
160 | b8929a54 | Max Filippov | |
161 | b8929a54 | Max Filippov | void *xtensa_get_extint(CPUState *env, unsigned extint) |
162 | b8929a54 | Max Filippov | { |
163 | b8929a54 | Max Filippov | if (extint < env->config->nextint) {
|
164 | b8929a54 | Max Filippov | unsigned irq = env->config->extint[extint];
|
165 | b8929a54 | Max Filippov | return env->irq_inputs[irq];
|
166 | b8929a54 | Max Filippov | } else {
|
167 | b8929a54 | Max Filippov | qemu_log("%s: trying to acquire invalid external interrupt %d\n",
|
168 | b8929a54 | Max Filippov | __func__, extint); |
169 | b8929a54 | Max Filippov | return NULL; |
170 | b8929a54 | Max Filippov | } |
171 | b8929a54 | Max Filippov | } |