]>
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 FB |
51 | |
52 | typedef struct SLAVIO_INTCTLState { | |
53 | uint32_t intreg_pending[MAX_CPUS]; | |
54 | uint32_t intregm_pending; | |
55 | uint32_t intregm_disabled; | |
56 | uint32_t target_cpu; | |
57 | #ifdef DEBUG_IRQ_COUNT | |
58 | uint64_t irq_count[32]; | |
59 | #endif | |
b3a23197 | 60 | qemu_irq *cpu_irqs[MAX_CPUS]; |
e0353fe2 | 61 | const uint32_t *intbit_to_level; |
e3a79bca | 62 | uint32_t cputimer_lbit, cputimer_mbit; |
b3a23197 | 63 | uint32_t pil_out[MAX_CPUS]; |
e80cfcfc FB |
64 | } SLAVIO_INTCTLState; |
65 | ||
66 | #define INTCTL_MAXADDR 0xf | |
5aca8c3b | 67 | #define INTCTL_SIZE (INTCTL_MAXADDR + 1) |
c6fdf5fc | 68 | #define INTCTLM_MAXADDR 0x13 |
5aca8c3b | 69 | #define INTCTLM_SIZE (INTCTLM_MAXADDR + 1) |
c6fdf5fc | 70 | #define INTCTLM_MASK 0x1f |
80be36b8 | 71 | #define MASTER_IRQ_MASK ~0x0fa2007f |
9a87ce9b | 72 | #define MASTER_DISABLE 0x80000000 |
6341fdcb BS |
73 | #define CPU_SOFTIRQ_MASK 0xfffe0000 |
74 | #define CPU_HARDIRQ_MASK 0x0000fffe | |
9a87ce9b BS |
75 | #define CPU_IRQ_INT15_IN 0x0004000 |
76 | #define CPU_IRQ_INT15_MASK 0x80000000 | |
77 | ||
66321a11 | 78 | static void slavio_check_interrupts(void *opaque); |
e80cfcfc FB |
79 | |
80 | // per-cpu interrupt controller | |
81 | static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr) | |
82 | { | |
83 | SLAVIO_INTCTLState *s = opaque; | |
dd4131b3 | 84 | uint32_t saddr, ret; |
e80cfcfc FB |
85 | int cpu; |
86 | ||
87 | cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12; | |
88 | saddr = (addr & INTCTL_MAXADDR) >> 2; | |
89 | switch (saddr) { | |
90 | case 0: | |
dd4131b3 BS |
91 | ret = s->intreg_pending[cpu]; |
92 | break; | |
e80cfcfc | 93 | default: |
dd4131b3 BS |
94 | ret = 0; |
95 | break; | |
e80cfcfc | 96 | } |
1569fc29 | 97 | DPRINTF("read cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, ret); |
dd4131b3 BS |
98 | |
99 | return ret; | |
e80cfcfc FB |
100 | } |
101 | ||
77f193da BS |
102 | static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, |
103 | uint32_t val) | |
e80cfcfc FB |
104 | { |
105 | SLAVIO_INTCTLState *s = opaque; | |
106 | uint32_t saddr; | |
107 | int cpu; | |
108 | ||
109 | cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12; | |
110 | saddr = (addr & INTCTL_MAXADDR) >> 2; | |
1569fc29 | 111 | DPRINTF("write cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, val); |
e80cfcfc FB |
112 | switch (saddr) { |
113 | case 1: // clear pending softints | |
9a87ce9b BS |
114 | if (val & CPU_IRQ_INT15_IN) |
115 | val |= CPU_IRQ_INT15_MASK; | |
6341fdcb | 116 | val &= CPU_SOFTIRQ_MASK; |
f930d07e | 117 | s->intreg_pending[cpu] &= ~val; |
327ac2e7 | 118 | slavio_check_interrupts(s); |
77f193da BS |
119 | DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", cpu, val, |
120 | s->intreg_pending[cpu]); | |
f930d07e | 121 | break; |
e80cfcfc | 122 | case 2: // set softint |
6341fdcb | 123 | val &= CPU_SOFTIRQ_MASK; |
f930d07e | 124 | s->intreg_pending[cpu] |= val; |
ba3c64fb | 125 | slavio_check_interrupts(s); |
77f193da BS |
126 | DPRINTF("Set cpu %d irq mask %x, curmask %x\n", cpu, val, |
127 | s->intreg_pending[cpu]); | |
f930d07e | 128 | break; |
e80cfcfc | 129 | default: |
f930d07e | 130 | break; |
e80cfcfc FB |
131 | } |
132 | } | |
133 | ||
134 | static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = { | |
7c560456 BS |
135 | NULL, |
136 | NULL, | |
e80cfcfc FB |
137 | slavio_intctl_mem_readl, |
138 | }; | |
139 | ||
140 | static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = { | |
7c560456 BS |
141 | NULL, |
142 | NULL, | |
e80cfcfc FB |
143 | slavio_intctl_mem_writel, |
144 | }; | |
145 | ||
146 | // master system interrupt controller | |
147 | static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr) | |
148 | { | |
149 | SLAVIO_INTCTLState *s = opaque; | |
dd4131b3 | 150 | uint32_t saddr, ret; |
e80cfcfc | 151 | |
cc2acc47 | 152 | saddr = (addr & INTCTLM_MASK) >> 2; |
e80cfcfc FB |
153 | switch (saddr) { |
154 | case 0: | |
9a87ce9b | 155 | ret = s->intregm_pending & ~MASTER_DISABLE; |
dd4131b3 | 156 | break; |
e80cfcfc | 157 | case 1: |
80be36b8 | 158 | ret = s->intregm_disabled & MASTER_IRQ_MASK; |
dd4131b3 | 159 | break; |
e80cfcfc | 160 | case 4: |
dd4131b3 BS |
161 | ret = s->target_cpu; |
162 | break; | |
e80cfcfc | 163 | default: |
dd4131b3 BS |
164 | ret = 0; |
165 | break; | |
e80cfcfc | 166 | } |
1569fc29 | 167 | DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); |
dd4131b3 BS |
168 | |
169 | return ret; | |
e80cfcfc FB |
170 | } |
171 | ||
77f193da BS |
172 | static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, |
173 | uint32_t val) | |
e80cfcfc FB |
174 | { |
175 | SLAVIO_INTCTLState *s = opaque; | |
176 | uint32_t saddr; | |
177 | ||
c6fdf5fc | 178 | saddr = (addr & INTCTLM_MASK) >> 2; |
1569fc29 | 179 | DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val); |
e80cfcfc FB |
180 | switch (saddr) { |
181 | case 2: // clear (enable) | |
f930d07e | 182 | // Force clear unused bits |
9a87ce9b | 183 | val &= MASTER_IRQ_MASK; |
f930d07e | 184 | s->intregm_disabled &= ~val; |
77f193da BS |
185 | DPRINTF("Enabled master irq mask %x, curmask %x\n", val, |
186 | s->intregm_disabled); | |
f930d07e BS |
187 | slavio_check_interrupts(s); |
188 | break; | |
e80cfcfc | 189 | case 3: // set (disable, clear pending) |
f930d07e | 190 | // Force clear unused bits |
9a87ce9b | 191 | val &= MASTER_IRQ_MASK; |
f930d07e BS |
192 | s->intregm_disabled |= val; |
193 | s->intregm_pending &= ~val; | |
327ac2e7 | 194 | slavio_check_interrupts(s); |
77f193da BS |
195 | DPRINTF("Disabled master irq mask %x, curmask %x\n", val, |
196 | s->intregm_disabled); | |
f930d07e | 197 | break; |
e80cfcfc | 198 | case 4: |
f930d07e | 199 | s->target_cpu = val & (MAX_CPUS - 1); |
327ac2e7 | 200 | slavio_check_interrupts(s); |
f930d07e BS |
201 | DPRINTF("Set master irq cpu %d\n", s->target_cpu); |
202 | break; | |
e80cfcfc | 203 | default: |
f930d07e | 204 | break; |
e80cfcfc FB |
205 | } |
206 | } | |
207 | ||
208 | static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = { | |
7c560456 BS |
209 | NULL, |
210 | NULL, | |
e80cfcfc FB |
211 | slavio_intctlm_mem_readl, |
212 | }; | |
213 | ||
214 | static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = { | |
7c560456 BS |
215 | NULL, |
216 | NULL, | |
e80cfcfc FB |
217 | slavio_intctlm_mem_writel, |
218 | }; | |
219 | ||
220 | void slavio_pic_info(void *opaque) | |
221 | { | |
222 | SLAVIO_INTCTLState *s = opaque; | |
223 | int i; | |
224 | ||
225 | for (i = 0; i < MAX_CPUS; i++) { | |
f930d07e | 226 | term_printf("per-cpu %d: pending 0x%08x\n", i, s->intreg_pending[i]); |
e80cfcfc | 227 | } |
77f193da BS |
228 | term_printf("master: pending 0x%08x, disabled 0x%08x\n", |
229 | s->intregm_pending, s->intregm_disabled); | |
e80cfcfc FB |
230 | } |
231 | ||
232 | void slavio_irq_info(void *opaque) | |
233 | { | |
234 | #ifndef DEBUG_IRQ_COUNT | |
235 | term_printf("irq statistic code not compiled.\n"); | |
236 | #else | |
237 | SLAVIO_INTCTLState *s = opaque; | |
238 | int i; | |
239 | int64_t count; | |
240 | ||
241 | term_printf("IRQ statistics:\n"); | |
242 | for (i = 0; i < 32; i++) { | |
243 | count = s->irq_count[i]; | |
244 | if (count > 0) | |
26a76461 | 245 | term_printf("%2d: %" PRId64 "\n", i, count); |
e80cfcfc FB |
246 | } |
247 | #endif | |
248 | } | |
249 | ||
66321a11 FB |
250 | static void slavio_check_interrupts(void *opaque) |
251 | { | |
252 | SLAVIO_INTCTLState *s = opaque; | |
327ac2e7 BS |
253 | uint32_t pending = s->intregm_pending, pil_pending; |
254 | unsigned int i, j; | |
66321a11 FB |
255 | |
256 | pending &= ~s->intregm_disabled; | |
257 | ||
b3a23197 | 258 | DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled); |
ba3c64fb | 259 | for (i = 0; i < MAX_CPUS; i++) { |
327ac2e7 | 260 | pil_pending = 0; |
9a87ce9b | 261 | if (pending && !(s->intregm_disabled & MASTER_DISABLE) && |
b3a23197 BS |
262 | (i == s->target_cpu)) { |
263 | for (j = 0; j < 32; j++) { | |
327ac2e7 BS |
264 | if (pending & (1 << j)) |
265 | pil_pending |= 1 << s->intbit_to_level[j]; | |
b3a23197 BS |
266 | } |
267 | } | |
6341fdcb | 268 | pil_pending |= (s->intreg_pending[i] & CPU_SOFTIRQ_MASK) >> 16; |
327ac2e7 BS |
269 | |
270 | for (j = 0; j < MAX_PILS; j++) { | |
271 | if (pil_pending & (1 << j)) { | |
272 | if (!(s->pil_out[i] & (1 << j))) | |
273 | qemu_irq_raise(s->cpu_irqs[i][j]); | |
274 | } else { | |
275 | if (s->pil_out[i] & (1 << j)) | |
276 | qemu_irq_lower(s->cpu_irqs[i][j]); | |
ba3c64fb FB |
277 | } |
278 | } | |
327ac2e7 | 279 | s->pil_out[i] = pil_pending; |
ba3c64fb | 280 | } |
66321a11 FB |
281 | } |
282 | ||
e80cfcfc FB |
283 | /* |
284 | * "irq" here is the bit number in the system interrupt register to | |
285 | * separate serial and keyboard interrupts sharing a level. | |
286 | */ | |
d7edfd27 | 287 | static void slavio_set_irq(void *opaque, int irq, int level) |
e80cfcfc FB |
288 | { |
289 | SLAVIO_INTCTLState *s = opaque; | |
b3a23197 BS |
290 | uint32_t mask = 1 << irq; |
291 | uint32_t pil = s->intbit_to_level[irq]; | |
292 | ||
293 | DPRINTF("Set cpu %d irq %d -> pil %d level %d\n", s->target_cpu, irq, pil, | |
294 | level); | |
295 | if (pil > 0) { | |
296 | if (level) { | |
327ac2e7 BS |
297 | #ifdef DEBUG_IRQ_COUNT |
298 | s->irq_count[pil]++; | |
299 | #endif | |
b3a23197 BS |
300 | s->intregm_pending |= mask; |
301 | s->intreg_pending[s->target_cpu] |= 1 << pil; | |
302 | } else { | |
303 | s->intregm_pending &= ~mask; | |
304 | s->intreg_pending[s->target_cpu] &= ~(1 << pil); | |
305 | } | |
306 | slavio_check_interrupts(s); | |
e80cfcfc FB |
307 | } |
308 | } | |
309 | ||
d7edfd27 | 310 | static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level) |
ba3c64fb FB |
311 | { |
312 | SLAVIO_INTCTLState *s = opaque; | |
313 | ||
b3a23197 | 314 | DPRINTF("Set cpu %d local timer level %d\n", cpu, level); |
d7edfd27 | 315 | |
e3a79bca BS |
316 | if (level) { |
317 | s->intregm_pending |= s->cputimer_mbit; | |
318 | s->intreg_pending[cpu] |= s->cputimer_lbit; | |
319 | } else { | |
320 | s->intregm_pending &= ~s->cputimer_mbit; | |
321 | s->intreg_pending[cpu] &= ~s->cputimer_lbit; | |
322 | } | |
d7edfd27 | 323 | |
ba3c64fb FB |
324 | slavio_check_interrupts(s); |
325 | } | |
326 | ||
e80cfcfc FB |
327 | static void slavio_intctl_save(QEMUFile *f, void *opaque) |
328 | { | |
329 | SLAVIO_INTCTLState *s = opaque; | |
330 | int i; | |
3b46e624 | 331 | |
e80cfcfc | 332 | for (i = 0; i < MAX_CPUS; i++) { |
f930d07e | 333 | qemu_put_be32s(f, &s->intreg_pending[i]); |
e80cfcfc FB |
334 | } |
335 | qemu_put_be32s(f, &s->intregm_pending); | |
336 | qemu_put_be32s(f, &s->intregm_disabled); | |
337 | qemu_put_be32s(f, &s->target_cpu); | |
338 | } | |
339 | ||
340 | static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id) | |
341 | { | |
342 | SLAVIO_INTCTLState *s = opaque; | |
343 | int i; | |
344 | ||
345 | if (version_id != 1) | |
346 | return -EINVAL; | |
347 | ||
348 | for (i = 0; i < MAX_CPUS; i++) { | |
f930d07e | 349 | qemu_get_be32s(f, &s->intreg_pending[i]); |
e80cfcfc FB |
350 | } |
351 | qemu_get_be32s(f, &s->intregm_pending); | |
352 | qemu_get_be32s(f, &s->intregm_disabled); | |
353 | qemu_get_be32s(f, &s->target_cpu); | |
327ac2e7 | 354 | slavio_check_interrupts(s); |
e80cfcfc FB |
355 | return 0; |
356 | } | |
357 | ||
358 | static void slavio_intctl_reset(void *opaque) | |
359 | { | |
360 | SLAVIO_INTCTLState *s = opaque; | |
361 | int i; | |
362 | ||
363 | for (i = 0; i < MAX_CPUS; i++) { | |
f930d07e | 364 | s->intreg_pending[i] = 0; |
e80cfcfc | 365 | } |
9a87ce9b | 366 | s->intregm_disabled = ~MASTER_IRQ_MASK; |
e80cfcfc FB |
367 | s->intregm_pending = 0; |
368 | s->target_cpu = 0; | |
327ac2e7 | 369 | slavio_check_interrupts(s); |
e80cfcfc FB |
370 | } |
371 | ||
5dcb6b91 | 372 | void *slavio_intctl_init(target_phys_addr_t addr, target_phys_addr_t addrg, |
d537cf6c | 373 | const uint32_t *intbit_to_level, |
d7edfd27 | 374 | qemu_irq **irq, qemu_irq **cpu_irq, |
b3a23197 | 375 | qemu_irq **parent_irq, unsigned int cputimer) |
e80cfcfc FB |
376 | { |
377 | int slavio_intctl_io_memory, slavio_intctlm_io_memory, i; | |
378 | SLAVIO_INTCTLState *s; | |
379 | ||
380 | s = qemu_mallocz(sizeof(SLAVIO_INTCTLState)); | |
381 | if (!s) | |
382 | return NULL; | |
383 | ||
e0353fe2 | 384 | s->intbit_to_level = intbit_to_level; |
e80cfcfc | 385 | for (i = 0; i < MAX_CPUS; i++) { |
77f193da BS |
386 | slavio_intctl_io_memory = cpu_register_io_memory(0, |
387 | slavio_intctl_mem_read, | |
388 | slavio_intctl_mem_write, | |
389 | s); | |
f930d07e | 390 | cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_SIZE, |
5aca8c3b | 391 | slavio_intctl_io_memory); |
b3a23197 | 392 | s->cpu_irqs[i] = parent_irq[i]; |
e80cfcfc FB |
393 | } |
394 | ||
77f193da BS |
395 | slavio_intctlm_io_memory = cpu_register_io_memory(0, |
396 | slavio_intctlm_mem_read, | |
397 | slavio_intctlm_mem_write, | |
398 | s); | |
5aca8c3b | 399 | cpu_register_physical_memory(addrg, INTCTLM_SIZE, slavio_intctlm_io_memory); |
e80cfcfc | 400 | |
77f193da BS |
401 | register_savevm("slavio_intctl", addr, 1, slavio_intctl_save, |
402 | slavio_intctl_load, s); | |
e80cfcfc | 403 | qemu_register_reset(slavio_intctl_reset, s); |
d537cf6c | 404 | *irq = qemu_allocate_irqs(slavio_set_irq, s, 32); |
d7edfd27 BS |
405 | |
406 | *cpu_irq = qemu_allocate_irqs(slavio_set_timer_irq_cpu, s, MAX_CPUS); | |
e3a79bca BS |
407 | s->cputimer_mbit = 1 << cputimer; |
408 | s->cputimer_lbit = 1 << intbit_to_level[cputimer]; | |
e80cfcfc FB |
409 | slavio_intctl_reset(s); |
410 | return s; | |
411 | } | |
412 |