]> Git Repo - qemu.git/blame - hw/intc/slavio_intctl.c
ioapic: introduce ioapic_entry_parse() helper
[qemu.git] / hw / intc / slavio_intctl.c
CommitLineData
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 */
a1961a4b 24
90191d07 25#include "qemu/osdep.h"
0d09e41a 26#include "hw/sparc/sun4m.h"
83c9089e 27#include "monitor/monitor.h"
83c9f4ca 28#include "hw/sysbus.h"
97bf4851 29#include "trace.h"
87ecb68b 30
e80cfcfc
FB
31//#define DEBUG_IRQ_COUNT
32
33/*
34 * Registers of interrupt controller in sun4m.
35 *
36 * This is the interrupt controller part of chip STP2001 (Slave I/O), also
37 * produced as NCR89C105. See
38 * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
39 *
40 * There is a system master controller and one for each cpu.
5fafdf24 41 *
e80cfcfc
FB
42 */
43
44#define MAX_CPUS 16
b3a23197 45#define MAX_PILS 16
e80cfcfc 46
a1961a4b
BS
47struct SLAVIO_INTCTLState;
48
49typedef struct SLAVIO_CPUINTCTLState {
8bb5ef33 50 MemoryRegion iomem;
a1961a4b 51 struct SLAVIO_INTCTLState *master;
07dd0035 52 uint32_t intreg_pending;
a1961a4b 53 uint32_t cpu;
462eda24 54 uint32_t irl_out;
a1961a4b 55} SLAVIO_CPUINTCTLState;
a8f48dcc 56
7abad863
AF
57#define TYPE_SLAVIO_INTCTL "slavio_intctl"
58#define SLAVIO_INTCTL(obj) \
59 OBJECT_CHECK(SLAVIO_INTCTLState, (obj), TYPE_SLAVIO_INTCTL)
60
e80cfcfc 61typedef struct SLAVIO_INTCTLState {
7abad863
AF
62 SysBusDevice parent_obj;
63
13c89a11 64 MemoryRegion iomem;
e80cfcfc
FB
65#ifdef DEBUG_IRQ_COUNT
66 uint64_t irq_count[32];
67#endif
a1961a4b 68 qemu_irq cpu_irqs[MAX_CPUS][MAX_PILS];
a1961a4b 69 SLAVIO_CPUINTCTLState slaves[MAX_CPUS];
07dd0035
BS
70 uint32_t intregm_pending;
71 uint32_t intregm_disabled;
72 uint32_t target_cpu;
e80cfcfc
FB
73} SLAVIO_INTCTLState;
74
75#define INTCTL_MAXADDR 0xf
5aca8c3b 76#define INTCTL_SIZE (INTCTL_MAXADDR + 1)
a8f48dcc 77#define INTCTLM_SIZE 0x14
80be36b8 78#define MASTER_IRQ_MASK ~0x0fa2007f
9a87ce9b 79#define MASTER_DISABLE 0x80000000
6341fdcb 80#define CPU_SOFTIRQ_MASK 0xfffe0000
462eda24
BS
81#define CPU_IRQ_INT15_IN (1 << 15)
82#define CPU_IRQ_TIMER_IN (1 << 14)
9a87ce9b 83
0d0a7e69 84static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs);
e80cfcfc
FB
85
86// per-cpu interrupt controller
a8170e5e 87static uint64_t slavio_intctl_mem_readl(void *opaque, hwaddr addr,
8bb5ef33 88 unsigned size)
e80cfcfc 89{
a8f48dcc 90 SLAVIO_CPUINTCTLState *s = opaque;
dd4131b3 91 uint32_t saddr, ret;
e80cfcfc 92
a8f48dcc 93 saddr = addr >> 2;
e80cfcfc
FB
94 switch (saddr) {
95 case 0:
a8f48dcc 96 ret = s->intreg_pending;
dd4131b3 97 break;
e80cfcfc 98 default:
dd4131b3
BS
99 ret = 0;
100 break;
e80cfcfc 101 }
97bf4851 102 trace_slavio_intctl_mem_readl(s->cpu, addr, ret);
dd4131b3
BS
103
104 return ret;
e80cfcfc
FB
105}
106
a8170e5e 107static void slavio_intctl_mem_writel(void *opaque, hwaddr addr,
8bb5ef33 108 uint64_t val, unsigned size)
e80cfcfc 109{
a8f48dcc 110 SLAVIO_CPUINTCTLState *s = opaque;
e80cfcfc 111 uint32_t saddr;
e80cfcfc 112
a8f48dcc 113 saddr = addr >> 2;
97bf4851 114 trace_slavio_intctl_mem_writel(s->cpu, addr, val);
e80cfcfc
FB
115 switch (saddr) {
116 case 1: // clear pending softints
462eda24 117 val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN;
a8f48dcc 118 s->intreg_pending &= ~val;
0d0a7e69 119 slavio_check_interrupts(s->master, 1);
97bf4851 120 trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending);
f930d07e 121 break;
e80cfcfc 122 case 2: // set softint
6341fdcb 123 val &= CPU_SOFTIRQ_MASK;
a8f48dcc 124 s->intreg_pending |= val;
0d0a7e69 125 slavio_check_interrupts(s->master, 1);
97bf4851 126 trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending);
f930d07e 127 break;
e80cfcfc 128 default:
f930d07e 129 break;
e80cfcfc
FB
130 }
131}
132
8bb5ef33
BC
133static const MemoryRegionOps slavio_intctl_mem_ops = {
134 .read = slavio_intctl_mem_readl,
135 .write = slavio_intctl_mem_writel,
136 .endianness = DEVICE_NATIVE_ENDIAN,
137 .valid = {
138 .min_access_size = 4,
139 .max_access_size = 4,
140 },
e80cfcfc
FB
141};
142
143// master system interrupt controller
a8170e5e 144static uint64_t slavio_intctlm_mem_readl(void *opaque, hwaddr addr,
13c89a11 145 unsigned size)
e80cfcfc
FB
146{
147 SLAVIO_INTCTLState *s = opaque;
dd4131b3 148 uint32_t saddr, ret;
e80cfcfc 149
a8f48dcc 150 saddr = addr >> 2;
e80cfcfc
FB
151 switch (saddr) {
152 case 0:
9a87ce9b 153 ret = s->intregm_pending & ~MASTER_DISABLE;
dd4131b3 154 break;
e80cfcfc 155 case 1:
80be36b8 156 ret = s->intregm_disabled & MASTER_IRQ_MASK;
dd4131b3 157 break;
e80cfcfc 158 case 4:
dd4131b3
BS
159 ret = s->target_cpu;
160 break;
e80cfcfc 161 default:
dd4131b3
BS
162 ret = 0;
163 break;
e80cfcfc 164 }
97bf4851 165 trace_slavio_intctlm_mem_readl(addr, ret);
dd4131b3
BS
166
167 return ret;
e80cfcfc
FB
168}
169
a8170e5e 170static void slavio_intctlm_mem_writel(void *opaque, hwaddr addr,
13c89a11 171 uint64_t val, unsigned size)
e80cfcfc
FB
172{
173 SLAVIO_INTCTLState *s = opaque;
174 uint32_t saddr;
175
a8f48dcc 176 saddr = addr >> 2;
97bf4851 177 trace_slavio_intctlm_mem_writel(addr, val);
e80cfcfc
FB
178 switch (saddr) {
179 case 2: // clear (enable)
f930d07e 180 // Force clear unused bits
9a87ce9b 181 val &= MASTER_IRQ_MASK;
f930d07e 182 s->intregm_disabled &= ~val;
97bf4851 183 trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled);
0d0a7e69 184 slavio_check_interrupts(s, 1);
f930d07e 185 break;
10760f0f 186 case 3: // set (disable; doesn't affect pending)
f930d07e 187 // Force clear unused bits
9a87ce9b 188 val &= MASTER_IRQ_MASK;
f930d07e 189 s->intregm_disabled |= val;
0d0a7e69 190 slavio_check_interrupts(s, 1);
97bf4851 191 trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled);
f930d07e 192 break;
e80cfcfc 193 case 4:
f930d07e 194 s->target_cpu = val & (MAX_CPUS - 1);
0d0a7e69 195 slavio_check_interrupts(s, 1);
97bf4851 196 trace_slavio_intctlm_mem_writel_target(s->target_cpu);
f930d07e 197 break;
e80cfcfc 198 default:
f930d07e 199 break;
e80cfcfc
FB
200 }
201}
202
13c89a11
BC
203static const MemoryRegionOps slavio_intctlm_mem_ops = {
204 .read = slavio_intctlm_mem_readl,
205 .write = slavio_intctlm_mem_writel,
206 .endianness = DEVICE_NATIVE_ENDIAN,
207 .valid = {
208 .min_access_size = 4,
209 .max_access_size = 4,
210 },
e80cfcfc
FB
211};
212
d453c2c3 213void slavio_pic_info(Monitor *mon, DeviceState *dev)
e80cfcfc 214{
7abad863 215 SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev);
e80cfcfc
FB
216 int i;
217
218 for (i = 0; i < MAX_CPUS; i++) {
376253ec 219 monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i,
a1961a4b 220 s->slaves[i].intreg_pending);
e80cfcfc 221 }
376253ec
AL
222 monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n",
223 s->intregm_pending, s->intregm_disabled);
e80cfcfc
FB
224}
225
d453c2c3 226void slavio_irq_info(Monitor *mon, DeviceState *dev)
e80cfcfc
FB
227{
228#ifndef DEBUG_IRQ_COUNT
376253ec 229 monitor_printf(mon, "irq statistic code not compiled.\n");
e80cfcfc 230#else
7abad863 231 SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev);
e80cfcfc
FB
232 int i;
233 int64_t count;
234
7abad863 235 s = SLAVIO_INTCTL(dev);
376253ec 236 monitor_printf(mon, "IRQ statistics:\n");
e80cfcfc
FB
237 for (i = 0; i < 32; i++) {
238 count = s->irq_count[i];
239 if (count > 0)
376253ec 240 monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
e80cfcfc
FB
241 }
242#endif
243}
244
68556e2e 245static const uint32_t intbit_to_level[] = {
462eda24
BS
246 2, 3, 5, 7, 9, 11, 13, 2, 3, 5, 7, 9, 11, 13, 12, 12,
247 6, 13, 4, 10, 8, 9, 11, 0, 0, 0, 0, 15, 15, 15, 15, 0,
68556e2e
BS
248};
249
0d0a7e69 250static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs)
66321a11 251{
327ac2e7
BS
252 uint32_t pending = s->intregm_pending, pil_pending;
253 unsigned int i, j;
66321a11
FB
254
255 pending &= ~s->intregm_disabled;
256
97bf4851 257 trace_slavio_check_interrupts(pending, s->intregm_disabled);
ba3c64fb 258 for (i = 0; i < MAX_CPUS; i++) {
327ac2e7 259 pil_pending = 0;
462eda24
BS
260
261 /* If we are the current interrupt target, get hard interrupts */
9a87ce9b 262 if (pending && !(s->intregm_disabled & MASTER_DISABLE) &&
b3a23197
BS
263 (i == s->target_cpu)) {
264 for (j = 0; j < 32; j++) {
462eda24 265 if ((pending & (1 << j)) && intbit_to_level[j]) {
68556e2e 266 pil_pending |= 1 << intbit_to_level[j];
462eda24
BS
267 }
268 }
269 }
270
271 /* Calculate current pending hard interrupts for display */
272 s->slaves[i].intreg_pending &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN |
273 CPU_IRQ_TIMER_IN;
274 if (i == s->target_cpu) {
275 for (j = 0; j < 32; j++) {
7d45e784 276 if ((s->intregm_pending & (1U << j)) && intbit_to_level[j]) {
462eda24
BS
277 s->slaves[i].intreg_pending |= 1 << intbit_to_level[j];
278 }
b3a23197
BS
279 }
280 }
462eda24 281
94c5f455
AT
282 /* Level 15 and CPU timer interrupts are only masked when
283 the MASTER_DISABLE bit is set */
284 if (!(s->intregm_disabled & MASTER_DISABLE)) {
285 pil_pending |= s->slaves[i].intreg_pending &
286 (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN);
287 }
462eda24
BS
288
289 /* Add soft interrupts */
a1961a4b 290 pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16;
327ac2e7 291
0d0a7e69 292 if (set_irqs) {
c84a88d8
PM
293 /* Since there is not really an interrupt 0 (and pil_pending
294 * and irl_out bit zero are thus always zero) there is no need
295 * to do anything with cpu_irqs[i][0] and it is OK not to do
296 * the j=0 iteration of this loop.
297 */
298 for (j = MAX_PILS-1; j > 0; j--) {
0d0a7e69 299 if (pil_pending & (1 << j)) {
462eda24 300 if (!(s->slaves[i].irl_out & (1 << j))) {
0d0a7e69
BS
301 qemu_irq_raise(s->cpu_irqs[i][j]);
302 }
303 } else {
462eda24 304 if (s->slaves[i].irl_out & (1 << j)) {
0d0a7e69
BS
305 qemu_irq_lower(s->cpu_irqs[i][j]);
306 }
307 }
ba3c64fb
FB
308 }
309 }
462eda24 310 s->slaves[i].irl_out = pil_pending;
ba3c64fb 311 }
66321a11
FB
312}
313
e80cfcfc
FB
314/*
315 * "irq" here is the bit number in the system interrupt register to
316 * separate serial and keyboard interrupts sharing a level.
317 */
d7edfd27 318static void slavio_set_irq(void *opaque, int irq, int level)
e80cfcfc
FB
319{
320 SLAVIO_INTCTLState *s = opaque;
b3a23197 321 uint32_t mask = 1 << irq;
68556e2e 322 uint32_t pil = intbit_to_level[irq];
462eda24 323 unsigned int i;
b3a23197 324
97bf4851 325 trace_slavio_set_irq(s->target_cpu, irq, pil, level);
b3a23197
BS
326 if (pil > 0) {
327 if (level) {
327ac2e7
BS
328#ifdef DEBUG_IRQ_COUNT
329 s->irq_count[pil]++;
330#endif
b3a23197 331 s->intregm_pending |= mask;
462eda24
BS
332 if (pil == 15) {
333 for (i = 0; i < MAX_CPUS; i++) {
334 s->slaves[i].intreg_pending |= 1 << pil;
335 }
336 }
b3a23197
BS
337 } else {
338 s->intregm_pending &= ~mask;
462eda24
BS
339 if (pil == 15) {
340 for (i = 0; i < MAX_CPUS; i++) {
341 s->slaves[i].intreg_pending &= ~(1 << pil);
342 }
343 }
b3a23197 344 }
0d0a7e69 345 slavio_check_interrupts(s, 1);
e80cfcfc
FB
346 }
347}
348
d7edfd27 349static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level)
ba3c64fb
FB
350{
351 SLAVIO_INTCTLState *s = opaque;
352
97bf4851 353 trace_slavio_set_timer_irq_cpu(cpu, level);
d7edfd27 354
e3a79bca 355 if (level) {
462eda24 356 s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN;
e3a79bca 357 } else {
462eda24 358 s->slaves[cpu].intreg_pending &= ~CPU_IRQ_TIMER_IN;
e3a79bca 359 }
d7edfd27 360
0d0a7e69 361 slavio_check_interrupts(s, 1);
ba3c64fb
FB
362}
363
a1961a4b
BS
364static void slavio_set_irq_all(void *opaque, int irq, int level)
365{
366 if (irq < 32) {
367 slavio_set_irq(opaque, irq, level);
368 } else {
369 slavio_set_timer_irq_cpu(opaque, irq - 32, level);
370 }
371}
372
e59fb374 373static int vmstate_intctl_post_load(void *opaque, int version_id)
e80cfcfc
FB
374{
375 SLAVIO_INTCTLState *s = opaque;
3b46e624 376
c9e95029
BS
377 slavio_check_interrupts(s, 0);
378 return 0;
e80cfcfc
FB
379}
380
c9e95029
BS
381static const VMStateDescription vmstate_intctl_cpu = {
382 .name ="slavio_intctl_cpu",
383 .version_id = 1,
384 .minimum_version_id = 1,
35d08458 385 .fields = (VMStateField[]) {
c9e95029
BS
386 VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState),
387 VMSTATE_END_OF_LIST()
388 }
389};
e80cfcfc 390
c9e95029
BS
391static const VMStateDescription vmstate_intctl = {
392 .name ="slavio_intctl",
393 .version_id = 1,
394 .minimum_version_id = 1,
752ff2fa 395 .post_load = vmstate_intctl_post_load,
35d08458 396 .fields = (VMStateField[]) {
c9e95029
BS
397 VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1,
398 vmstate_intctl_cpu, SLAVIO_CPUINTCTLState),
399 VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState),
400 VMSTATE_UINT32(intregm_disabled, SLAVIO_INTCTLState),
401 VMSTATE_UINT32(target_cpu, SLAVIO_INTCTLState),
402 VMSTATE_END_OF_LIST()
e80cfcfc 403 }
c9e95029 404};
e80cfcfc 405
78971d57 406static void slavio_intctl_reset(DeviceState *d)
e80cfcfc 407{
7abad863 408 SLAVIO_INTCTLState *s = SLAVIO_INTCTL(d);
e80cfcfc
FB
409 int i;
410
411 for (i = 0; i < MAX_CPUS; i++) {
a1961a4b 412 s->slaves[i].intreg_pending = 0;
462eda24 413 s->slaves[i].irl_out = 0;
e80cfcfc 414 }
9a87ce9b 415 s->intregm_disabled = ~MASTER_IRQ_MASK;
e80cfcfc
FB
416 s->intregm_pending = 0;
417 s->target_cpu = 0;
0d0a7e69 418 slavio_check_interrupts(s, 0);
e80cfcfc
FB
419}
420
c09008d2 421static void slavio_intctl_init(Object *obj)
e80cfcfc 422{
c09008d2
XZ
423 DeviceState *dev = DEVICE(obj);
424 SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj);
425 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
a1961a4b 426 unsigned int i, j;
8bb5ef33 427 char slave_name[45];
e80cfcfc 428
7abad863 429 qdev_init_gpio_in(dev, slavio_set_irq_all, 32 + MAX_CPUS);
c09008d2 430 memory_region_init_io(&s->iomem, obj, &slavio_intctlm_mem_ops, s,
13c89a11 431 "master-interrupt-controller", INTCTLM_SIZE);
7abad863 432 sysbus_init_mmio(sbd, &s->iomem);
e80cfcfc
FB
433
434 for (i = 0; i < MAX_CPUS; i++) {
8bb5ef33
BC
435 snprintf(slave_name, sizeof(slave_name),
436 "slave-interrupt-controller-%i", i);
a1961a4b 437 for (j = 0; j < MAX_PILS; j++) {
7abad863 438 sysbus_init_irq(sbd, &s->cpu_irqs[i][j]);
a1961a4b 439 }
1437c94b
PB
440 memory_region_init_io(&s->slaves[i].iomem, OBJECT(s),
441 &slavio_intctl_mem_ops,
8bb5ef33 442 &s->slaves[i], slave_name, INTCTL_SIZE);
7abad863 443 sysbus_init_mmio(sbd, &s->slaves[i].iomem);
a1961a4b
BS
444 s->slaves[i].cpu = i;
445 s->slaves[i].master = s;
446 }
a1961a4b
BS
447}
448
999e12bb
AL
449static void slavio_intctl_class_init(ObjectClass *klass, void *data)
450{
39bffca2 451 DeviceClass *dc = DEVICE_CLASS(klass);
999e12bb 452
39bffca2
AL
453 dc->reset = slavio_intctl_reset;
454 dc->vmsd = &vmstate_intctl;
999e12bb
AL
455}
456
8c43a6f0 457static const TypeInfo slavio_intctl_info = {
7abad863 458 .name = TYPE_SLAVIO_INTCTL,
39bffca2
AL
459 .parent = TYPE_SYS_BUS_DEVICE,
460 .instance_size = sizeof(SLAVIO_INTCTLState),
c09008d2 461 .instance_init = slavio_intctl_init,
39bffca2 462 .class_init = slavio_intctl_class_init,
a1961a4b 463};
d7edfd27 464
83f7d43a 465static void slavio_intctl_register_types(void)
a1961a4b 466{
39bffca2 467 type_register_static(&slavio_intctl_info);
e80cfcfc 468}
a1961a4b 469
83f7d43a 470type_init(slavio_intctl_register_types)
This page took 0.979968 seconds and 4 git commands to generate.