Merge branch 'master' of git://git.qemu.org/qemu
[qemu.git] / hw / syborg_interrupt.c
blob93e81c8660eccbc0c1416642a9bff3f54b462d68
1 /*
2 * Syborg interrupt controller.
4 * Copyright (c) 2008 CodeSourcery
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
25 #include "sysbus.h"
26 #include "syborg.h"
28 //#define DEBUG_SYBORG_INT
30 #ifdef DEBUG_SYBORG_INT
31 #define DPRINTF(fmt, ...) \
32 do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0)
33 #define BADF(fmt, ...) \
34 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \
35 exit(1);} while (0)
36 #else
37 #define DPRINTF(fmt, ...) do {} while(0)
38 #define BADF(fmt, ...) \
39 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0)
40 #endif
41 enum {
42 INT_ID = 0,
43 INT_STATUS = 1, /* number of pending interrupts */
44 INT_CURRENT = 2, /* next interrupt to be serviced */
45 INT_DISABLE_ALL = 3,
46 INT_DISABLE = 4,
47 INT_ENABLE = 5,
48 INT_TOTAL = 6
51 typedef struct {
52 unsigned level:1;
53 unsigned enabled:1;
54 } syborg_int_flags;
56 typedef struct {
57 SysBusDevice busdev;
58 MemoryRegion iomem;
59 int pending_count;
60 uint32_t num_irqs;
61 syborg_int_flags *flags;
62 qemu_irq parent_irq;
63 } SyborgIntState;
65 static void syborg_int_update(SyborgIntState *s)
67 DPRINTF("pending %d\n", s->pending_count);
68 qemu_set_irq(s->parent_irq, s->pending_count > 0);
71 static void syborg_int_set_irq(void *opaque, int irq, int level)
73 SyborgIntState *s = (SyborgIntState *)opaque;
75 if (s->flags[irq].level == level)
76 return;
78 s->flags[irq].level = level;
79 if (s->flags[irq].enabled) {
80 if (level)
81 s->pending_count++;
82 else
83 s->pending_count--;
84 syborg_int_update(s);
88 static uint64_t syborg_int_read(void *opaque, target_phys_addr_t offset,
89 unsigned size)
91 SyborgIntState *s = (SyborgIntState *)opaque;
92 int i;
94 offset &= 0xfff;
95 switch (offset >> 2) {
96 case INT_ID:
97 return SYBORG_ID_INT;
98 case INT_STATUS:
99 DPRINTF("read status=%d\n", s->pending_count);
100 return s->pending_count;
102 case INT_CURRENT:
103 for (i = 0; i < s->num_irqs; i++) {
104 if (s->flags[i].level & s->flags[i].enabled) {
105 DPRINTF("read current=%d\n", i);
106 return i;
109 DPRINTF("read current=none\n");
110 return 0xffffffffu;
112 default:
113 cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
114 (int)offset);
115 return 0;
119 static void syborg_int_write(void *opaque, target_phys_addr_t offset,
120 uint64_t value, unsigned size)
122 SyborgIntState *s = (SyborgIntState *)opaque;
123 int i;
124 offset &= 0xfff;
126 DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value);
127 switch (offset >> 2) {
128 case INT_DISABLE_ALL:
129 s->pending_count = 0;
130 for (i = 0; i < s->num_irqs; i++)
131 s->flags[i].enabled = 0;
132 break;
134 case INT_DISABLE:
135 if (value >= s->num_irqs)
136 break;
137 if (s->flags[value].enabled) {
138 if (s->flags[value].enabled)
139 s->pending_count--;
140 s->flags[value].enabled = 0;
142 break;
144 case INT_ENABLE:
145 if (value >= s->num_irqs)
146 break;
147 if (!(s->flags[value].enabled)) {
148 if(s->flags[value].level)
149 s->pending_count++;
150 s->flags[value].enabled = 1;
152 break;
154 default:
155 cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
156 (int)offset);
157 return;
159 syborg_int_update(s);
162 static const MemoryRegionOps syborg_int_ops = {
163 .read = syborg_int_read,
164 .write = syborg_int_write,
165 .endianness = DEVICE_NATIVE_ENDIAN,
168 static void syborg_int_save(QEMUFile *f, void *opaque)
170 SyborgIntState *s = (SyborgIntState *)opaque;
171 int i;
173 qemu_put_be32(f, s->num_irqs);
174 qemu_put_be32(f, s->pending_count);
175 for (i = 0; i < s->num_irqs; i++) {
176 qemu_put_be32(f, s->flags[i].enabled
177 | ((unsigned)s->flags[i].level << 1));
181 static int syborg_int_load(QEMUFile *f, void *opaque, int version_id)
183 SyborgIntState *s = (SyborgIntState *)opaque;
184 uint32_t val;
185 int i;
187 if (version_id != 1)
188 return -EINVAL;
190 val = qemu_get_be32(f);
191 if (val != s->num_irqs)
192 return -EINVAL;
193 s->pending_count = qemu_get_be32(f);
194 for (i = 0; i < s->num_irqs; i++) {
195 val = qemu_get_be32(f);
196 s->flags[i].enabled = val & 1;
197 s->flags[i].level = (val >> 1) & 1;
199 return 0;
202 static int syborg_int_init(SysBusDevice *dev)
204 SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev);
206 sysbus_init_irq(dev, &s->parent_irq);
207 qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs);
208 memory_region_init_io(&s->iomem, &syborg_int_ops, s,
209 "interrupt", 0x1000);
210 sysbus_init_mmio(dev, &s->iomem);
211 s->flags = g_malloc0(s->num_irqs * sizeof(syborg_int_flags));
213 register_savevm(&dev->qdev, "syborg_int", -1, 1, syborg_int_save,
214 syborg_int_load, s);
215 return 0;
218 static SysBusDeviceInfo syborg_int_info = {
219 .init = syborg_int_init,
220 .qdev.name = "syborg,interrupt",
221 .qdev.size = sizeof(SyborgIntState),
222 .qdev.props = (Property[]) {
223 DEFINE_PROP_UINT32("num-interrupts", SyborgIntState, num_irqs, 64),
224 DEFINE_PROP_END_OF_LIST(),
228 static void syborg_interrupt_register_devices(void)
230 sysbus_register_withprop(&syborg_int_info);
233 device_init(syborg_interrupt_register_devices)