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