]>
Commit | Line | Data |
---|---|---|
e80cfcfc FB |
1 | /* |
2 | * QEMU Sparc SLAVIO interrupt controller emulation | |
5fafdf24 | 3 | * |
66321a11 | 4 | * Copyright (c) 2003-2005 Fabrice Bellard |
5fafdf24 | 5 | * |
e80cfcfc FB |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
87ecb68b PB |
24 | #include "hw.h" |
25 | #include "sun4m.h" | |
26 | #include "console.h" | |
27 | ||
e80cfcfc | 28 | //#define DEBUG_IRQ_COUNT |
66321a11 FB |
29 | //#define DEBUG_IRQ |
30 | ||
31 | #ifdef DEBUG_IRQ | |
32 | #define DPRINTF(fmt, args...) \ | |
33 | do { printf("IRQ: " fmt , ##args); } while (0) | |
34 | #else | |
35 | #define DPRINTF(fmt, args...) | |
36 | #endif | |
e80cfcfc FB |
37 | |
38 | /* | |
39 | * Registers of interrupt controller in sun4m. | |
40 | * | |
41 | * This is the interrupt controller part of chip STP2001 (Slave I/O), also | |
42 | * produced as NCR89C105. See | |
43 | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt | |
44 | * | |
45 | * There is a system master controller and one for each cpu. | |
5fafdf24 | 46 | * |
e80cfcfc FB |
47 | */ |
48 | ||
49 | #define MAX_CPUS 16 | |
b3a23197 | 50 | #define MAX_PILS 16 |
e80cfcfc | 51 | |
a8f48dcc BS |
52 | struct SLAVIO_CPUINTCTLState; |
53 | ||
e80cfcfc | 54 | typedef struct SLAVIO_INTCTLState { |
e80cfcfc FB |
55 | uint32_t intregm_pending; |
56 | uint32_t intregm_disabled; | |
57 | uint32_t target_cpu; | |
58 | #ifdef DEBUG_IRQ_COUNT | |
59 | uint64_t irq_count[32]; | |
60 | #endif | |
b3a23197 | 61 | qemu_irq *cpu_irqs[MAX_CPUS]; |
e0353fe2 | 62 | const uint32_t *intbit_to_level; |
e3a79bca | 63 | uint32_t cputimer_lbit, cputimer_mbit; |
b3a23197 | 64 | uint32_t pil_out[MAX_CPUS]; |
a8f48dcc | 65 | struct SLAVIO_CPUINTCTLState *slaves[MAX_CPUS]; |
e80cfcfc FB |
66 | } SLAVIO_INTCTLState; |
67 | ||
a8f48dcc BS |
68 | typedef struct SLAVIO_CPUINTCTLState { |
69 | uint32_t intreg_pending; | |
70 | SLAVIO_INTCTLState *master; | |
71 | uint32_t cpu; | |
72 | } SLAVIO_CPUINTCTLState; | |
73 | ||
e80cfcfc | 74 | #define INTCTL_MAXADDR 0xf |
5aca8c3b | 75 | #define INTCTL_SIZE (INTCTL_MAXADDR + 1) |
a8f48dcc | 76 | #define INTCTLM_SIZE 0x14 |
80be36b8 | 77 | #define MASTER_IRQ_MASK ~0x0fa2007f |
9a87ce9b | 78 | #define MASTER_DISABLE 0x80000000 |
6341fdcb BS |
79 | #define CPU_SOFTIRQ_MASK 0xfffe0000 |
80 | #define CPU_HARDIRQ_MASK 0x0000fffe | |
9a87ce9b BS |
81 | #define CPU_IRQ_INT15_IN 0x0004000 |
82 | #define CPU_IRQ_INT15_MASK 0x80000000 | |
83 | ||
a8f48dcc | 84 | static void slavio_check_interrupts(SLAVIO_INTCTLState *s); |
e80cfcfc FB |
85 | |
86 | // per-cpu interrupt controller | |
87 | static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr) | |
88 | { | |
a8f48dcc | 89 | SLAVIO_CPUINTCTLState *s = opaque; |
dd4131b3 | 90 | uint32_t saddr, ret; |
e80cfcfc | 91 | |
a8f48dcc | 92 | saddr = addr >> 2; |
e80cfcfc FB |
93 | switch (saddr) { |
94 | case 0: | |
a8f48dcc | 95 | ret = s->intreg_pending; |
dd4131b3 | 96 | break; |
e80cfcfc | 97 | default: |
dd4131b3 BS |
98 | ret = 0; |
99 | break; | |
e80cfcfc | 100 | } |
1569fc29 | 101 | DPRINTF("read cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, ret); |
dd4131b3 BS |
102 | |
103 | return ret; | |
e80cfcfc FB |
104 | } |
105 | ||
77f193da BS |
106 | static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, |
107 | uint32_t val) | |
e80cfcfc | 108 | { |
a8f48dcc | 109 | SLAVIO_CPUINTCTLState *s = opaque; |
e80cfcfc | 110 | uint32_t saddr; |
e80cfcfc | 111 | |
a8f48dcc | 112 | saddr = addr >> 2; |
1569fc29 | 113 | DPRINTF("write cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, val); |
e80cfcfc FB |
114 | switch (saddr) { |
115 | case 1: // clear pending softints | |
9a87ce9b BS |
116 | if (val & CPU_IRQ_INT15_IN) |
117 | val |= CPU_IRQ_INT15_MASK; | |
6341fdcb | 118 | val &= CPU_SOFTIRQ_MASK; |
a8f48dcc BS |
119 | s->intreg_pending &= ~val; |
120 | slavio_check_interrupts(s->master); | |
121 | DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", s->cpu, val, | |
122 | s->intreg_pending); | |
f930d07e | 123 | break; |
e80cfcfc | 124 | case 2: // set softint |
6341fdcb | 125 | val &= CPU_SOFTIRQ_MASK; |
a8f48dcc BS |
126 | s->intreg_pending |= val; |
127 | slavio_check_interrupts(s->master); | |
128 | DPRINTF("Set cpu %d irq mask %x, curmask %x\n", s->cpu, val, | |
129 | s->intreg_pending); | |
f930d07e | 130 | break; |
e80cfcfc | 131 | default: |
f930d07e | 132 | break; |
e80cfcfc FB |
133 | } |
134 | } | |
135 | ||
136 | static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = { | |
7c560456 BS |
137 | NULL, |
138 | NULL, | |
e80cfcfc FB |
139 | slavio_intctl_mem_readl, |
140 | }; | |
141 | ||
142 | static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = { | |
7c560456 BS |
143 | NULL, |
144 | NULL, | |
e80cfcfc FB |
145 | slavio_intctl_mem_writel, |
146 | }; | |
147 | ||
148 | // master system interrupt controller | |
149 | static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr) | |
150 | { | |
151 | SLAVIO_INTCTLState *s = opaque; | |
dd4131b3 | 152 | uint32_t saddr, ret; |
e80cfcfc | 153 | |
a8f48dcc | 154 | saddr = addr >> 2; |
e80cfcfc FB |
155 | switch (saddr) { |
156 | case 0: | |
9a87ce9b | 157 | ret = s->intregm_pending & ~MASTER_DISABLE; |
dd4131b3 | 158 | break; |
e80cfcfc | 159 | case 1: |
80be36b8 | 160 | ret = s->intregm_disabled & MASTER_IRQ_MASK; |
dd4131b3 | 161 | break; |
e80cfcfc | 162 | case 4: |
dd4131b3 BS |
163 | ret = s->target_cpu; |
164 | break; | |
e80cfcfc | 165 | default: |
dd4131b3 BS |
166 | ret = 0; |
167 | break; | |
e80cfcfc | 168 | } |
1569fc29 | 169 | DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); |
dd4131b3 BS |
170 | |
171 | return ret; | |
e80cfcfc FB |
172 | } |
173 | ||
77f193da BS |
174 | static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, |
175 | uint32_t val) | |
e80cfcfc FB |
176 | { |
177 | SLAVIO_INTCTLState *s = opaque; | |
178 | uint32_t saddr; | |
179 | ||
a8f48dcc | 180 | saddr = addr >> 2; |
1569fc29 | 181 | DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val); |
e80cfcfc FB |
182 | switch (saddr) { |
183 | case 2: // clear (enable) | |
f930d07e | 184 | // Force clear unused bits |
9a87ce9b | 185 | val &= MASTER_IRQ_MASK; |
f930d07e | 186 | s->intregm_disabled &= ~val; |
77f193da BS |
187 | DPRINTF("Enabled master irq mask %x, curmask %x\n", val, |
188 | s->intregm_disabled); | |
f930d07e BS |
189 | slavio_check_interrupts(s); |
190 | break; | |
e80cfcfc | 191 | case 3: // set (disable, clear pending) |
f930d07e | 192 | // Force clear unused bits |
9a87ce9b | 193 | val &= MASTER_IRQ_MASK; |
f930d07e BS |
194 | s->intregm_disabled |= val; |
195 | s->intregm_pending &= ~val; | |
327ac2e7 | 196 | slavio_check_interrupts(s); |
77f193da BS |
197 | DPRINTF("Disabled master irq mask %x, curmask %x\n", val, |
198 | s->intregm_disabled); | |
f930d07e | 199 | break; |
e80cfcfc | 200 | case 4: |
f930d07e | 201 | s->target_cpu = val & (MAX_CPUS - 1); |
327ac2e7 | 202 | slavio_check_interrupts(s); |
f930d07e BS |
203 | DPRINTF("Set master irq cpu %d\n", s->target_cpu); |
204 | break; | |
e80cfcfc | 205 | default: |
f930d07e | 206 | break; |
e80cfcfc FB |
207 | } |
208 | } | |
209 | ||
210 | static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = { | |
7c560456 BS |
211 | NULL, |
212 | NULL, | |
e80cfcfc FB |
213 | slavio_intctlm_mem_readl, |
214 | }; | |
215 | ||
216 | static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = { | |
7c560456 BS |
217 | NULL, |
218 | NULL, | |
e80cfcfc FB |
219 | slavio_intctlm_mem_writel, |
220 | }; | |
221 | ||
222 | void slavio_pic_info(void *opaque) | |
223 | { | |
224 | SLAVIO_INTCTLState *s = opaque; | |
225 | int i; | |
226 | ||
227 | for (i = 0; i < MAX_CPUS; i++) { | |
a8f48dcc BS |
228 | term_printf("per-cpu %d: pending 0x%08x\n", i, |
229 | s->slaves[i]->intreg_pending); | |
e80cfcfc | 230 | } |
77f193da BS |
231 | term_printf("master: pending 0x%08x, disabled 0x%08x\n", |
232 | s->intregm_pending, s->intregm_disabled); | |
e80cfcfc FB |
233 | } |
234 | ||
235 | void slavio_irq_info(void *opaque) | |
236 | { | |
237 | #ifndef DEBUG_IRQ_COUNT | |
238 | term_printf("irq statistic code not compiled.\n"); | |
239 | #else | |
240 | SLAVIO_INTCTLState *s = opaque; | |
241 | int i; | |
242 | int64_t count; | |
243 | ||
244 | term_printf("IRQ statistics:\n"); | |
245 | for (i = 0; i < 32; i++) { | |
246 | count = s->irq_count[i]; | |
247 | if (count > 0) | |
26a76461 | 248 | term_printf("%2d: %" PRId64 "\n", i, count); |
e80cfcfc FB |
249 | } |
250 | #endif | |
251 | } | |
252 | ||
a8f48dcc | 253 | static void slavio_check_interrupts(SLAVIO_INTCTLState *s) |
66321a11 | 254 | { |
327ac2e7 BS |
255 | uint32_t pending = s->intregm_pending, pil_pending; |
256 | unsigned int i, j; | |
66321a11 FB |
257 | |
258 | pending &= ~s->intregm_disabled; | |
259 | ||
b3a23197 | 260 | DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled); |
ba3c64fb | 261 | for (i = 0; i < MAX_CPUS; i++) { |
327ac2e7 | 262 | pil_pending = 0; |
9a87ce9b | 263 | if (pending && !(s->intregm_disabled & MASTER_DISABLE) && |
b3a23197 BS |
264 | (i == s->target_cpu)) { |
265 | for (j = 0; j < 32; j++) { | |
327ac2e7 BS |
266 | if (pending & (1 << j)) |
267 | pil_pending |= 1 << s->intbit_to_level[j]; | |
b3a23197 BS |
268 | } |
269 | } | |
a8f48dcc | 270 | pil_pending |= (s->slaves[i]->intreg_pending & CPU_SOFTIRQ_MASK) >> 16; |
327ac2e7 BS |
271 | |
272 | for (j = 0; j < MAX_PILS; j++) { | |
273 | if (pil_pending & (1 << j)) { | |
274 | if (!(s->pil_out[i] & (1 << j))) | |
275 | qemu_irq_raise(s->cpu_irqs[i][j]); | |
276 | } else { | |
277 | if (s->pil_out[i] & (1 << j)) | |
278 | qemu_irq_lower(s->cpu_irqs[i][j]); | |
ba3c64fb FB |
279 | } |
280 | } | |
327ac2e7 | 281 | s->pil_out[i] = pil_pending; |
ba3c64fb | 282 | } |
66321a11 FB |
283 | } |
284 | ||
e80cfcfc FB |
285 | /* |
286 | * "irq" here is the bit number in the system interrupt register to | |
287 | * separate serial and keyboard interrupts sharing a level. | |
288 | */ | |
d7edfd27 | 289 | static void slavio_set_irq(void *opaque, int irq, int level) |
e80cfcfc FB |
290 | { |
291 | SLAVIO_INTCTLState *s = opaque; | |
b3a23197 BS |
292 | uint32_t mask = 1 << irq; |
293 | uint32_t pil = s->intbit_to_level[irq]; | |
294 | ||
295 | DPRINTF("Set cpu %d irq %d -> pil %d level %d\n", s->target_cpu, irq, pil, | |
296 | level); | |
297 | if (pil > 0) { | |
298 | if (level) { | |
327ac2e7 BS |
299 | #ifdef DEBUG_IRQ_COUNT |
300 | s->irq_count[pil]++; | |
301 | #endif | |
b3a23197 | 302 | s->intregm_pending |= mask; |
a8f48dcc | 303 | s->slaves[s->target_cpu]->intreg_pending |= 1 << pil; |
b3a23197 BS |
304 | } else { |
305 | s->intregm_pending &= ~mask; | |
a8f48dcc | 306 | s->slaves[s->target_cpu]->intreg_pending &= ~(1 << pil); |
b3a23197 BS |
307 | } |
308 | slavio_check_interrupts(s); | |
e80cfcfc FB |
309 | } |
310 | } | |
311 | ||
d7edfd27 | 312 | static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level) |
ba3c64fb FB |
313 | { |
314 | SLAVIO_INTCTLState *s = opaque; | |
315 | ||
b3a23197 | 316 | DPRINTF("Set cpu %d local timer level %d\n", cpu, level); |
d7edfd27 | 317 | |
e3a79bca BS |
318 | if (level) { |
319 | s->intregm_pending |= s->cputimer_mbit; | |
a8f48dcc | 320 | s->slaves[cpu]->intreg_pending |= s->cputimer_lbit; |
e3a79bca BS |
321 | } else { |
322 | s->intregm_pending &= ~s->cputimer_mbit; | |
a8f48dcc | 323 | s->slaves[cpu]->intreg_pending &= ~s->cputimer_lbit; |
e3a79bca | 324 | } |
d7edfd27 | 325 | |
ba3c64fb FB |
326 | slavio_check_interrupts(s); |
327 | } | |
328 | ||
e80cfcfc FB |
329 | static void slavio_intctl_save(QEMUFile *f, void *opaque) |
330 | { | |
331 | SLAVIO_INTCTLState *s = opaque; | |
332 | int i; | |
3b46e624 | 333 | |
e80cfcfc | 334 | for (i = 0; i < MAX_CPUS; i++) { |
a8f48dcc | 335 | qemu_put_be32s(f, &s->slaves[i]->intreg_pending); |
e80cfcfc FB |
336 | } |
337 | qemu_put_be32s(f, &s->intregm_pending); | |
338 | qemu_put_be32s(f, &s->intregm_disabled); | |
339 | qemu_put_be32s(f, &s->target_cpu); | |
340 | } | |
341 | ||
342 | static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id) | |
343 | { | |
344 | SLAVIO_INTCTLState *s = opaque; | |
345 | int i; | |
346 | ||
347 | if (version_id != 1) | |
348 | return -EINVAL; | |
349 | ||
350 | for (i = 0; i < MAX_CPUS; i++) { | |
a8f48dcc | 351 | qemu_get_be32s(f, &s->slaves[i]->intreg_pending); |
e80cfcfc FB |
352 | } |
353 | qemu_get_be32s(f, &s->intregm_pending); | |
354 | qemu_get_be32s(f, &s->intregm_disabled); | |
355 | qemu_get_be32s(f, &s->target_cpu); | |
327ac2e7 | 356 | slavio_check_interrupts(s); |
e80cfcfc FB |
357 | return 0; |
358 | } | |
359 | ||
360 | static void slavio_intctl_reset(void *opaque) | |
361 | { | |
362 | SLAVIO_INTCTLState *s = opaque; | |
363 | int i; | |
364 | ||
365 | for (i = 0; i < MAX_CPUS; i++) { | |
a8f48dcc | 366 | s->slaves[i]->intreg_pending = 0; |
e80cfcfc | 367 | } |
9a87ce9b | 368 | s->intregm_disabled = ~MASTER_IRQ_MASK; |
e80cfcfc FB |
369 | s->intregm_pending = 0; |
370 | s->target_cpu = 0; | |
327ac2e7 | 371 | slavio_check_interrupts(s); |
e80cfcfc FB |
372 | } |
373 | ||
5dcb6b91 | 374 | void *slavio_intctl_init(target_phys_addr_t addr, target_phys_addr_t addrg, |
d537cf6c | 375 | const uint32_t *intbit_to_level, |
d7edfd27 | 376 | qemu_irq **irq, qemu_irq **cpu_irq, |
b3a23197 | 377 | qemu_irq **parent_irq, unsigned int cputimer) |
e80cfcfc FB |
378 | { |
379 | int slavio_intctl_io_memory, slavio_intctlm_io_memory, i; | |
380 | SLAVIO_INTCTLState *s; | |
a8f48dcc | 381 | SLAVIO_CPUINTCTLState *slave; |
e80cfcfc FB |
382 | |
383 | s = qemu_mallocz(sizeof(SLAVIO_INTCTLState)); | |
384 | if (!s) | |
385 | return NULL; | |
386 | ||
e0353fe2 | 387 | s->intbit_to_level = intbit_to_level; |
e80cfcfc | 388 | for (i = 0; i < MAX_CPUS; i++) { |
a8f48dcc BS |
389 | slave = qemu_mallocz(sizeof(SLAVIO_CPUINTCTLState)); |
390 | if (!slave) | |
391 | return NULL; | |
392 | ||
393 | slave->cpu = i; | |
394 | slave->master = s; | |
395 | ||
77f193da BS |
396 | slavio_intctl_io_memory = cpu_register_io_memory(0, |
397 | slavio_intctl_mem_read, | |
398 | slavio_intctl_mem_write, | |
a8f48dcc BS |
399 | slave); |
400 | cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_SIZE, | |
401 | slavio_intctl_io_memory); | |
402 | ||
403 | s->slaves[i] = slave; | |
b3a23197 | 404 | s->cpu_irqs[i] = parent_irq[i]; |
e80cfcfc FB |
405 | } |
406 | ||
77f193da BS |
407 | slavio_intctlm_io_memory = cpu_register_io_memory(0, |
408 | slavio_intctlm_mem_read, | |
409 | slavio_intctlm_mem_write, | |
410 | s); | |
5aca8c3b | 411 | cpu_register_physical_memory(addrg, INTCTLM_SIZE, slavio_intctlm_io_memory); |
e80cfcfc | 412 | |
77f193da BS |
413 | register_savevm("slavio_intctl", addr, 1, slavio_intctl_save, |
414 | slavio_intctl_load, s); | |
e80cfcfc | 415 | qemu_register_reset(slavio_intctl_reset, s); |
d537cf6c | 416 | *irq = qemu_allocate_irqs(slavio_set_irq, s, 32); |
d7edfd27 BS |
417 | |
418 | *cpu_irq = qemu_allocate_irqs(slavio_set_timer_irq_cpu, s, MAX_CPUS); | |
e3a79bca BS |
419 | s->cputimer_mbit = 1 << cputimer; |
420 | s->cputimer_lbit = 1 << intbit_to_level[cputimer]; | |
e80cfcfc FB |
421 | slavio_intctl_reset(s); |
422 | return s; | |
423 | } |