Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / syborg_interrupt.c
blob82edd3d3caa49a519189f70c9c5f166d53078c41
1 /*
2 * Syborg interrupt controller.
4 * Copyright (c) 2008 CodeSourcery
5 * Copyright (c) 2010, 2013 Stefan Weil
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "syborg.h"
30 //#define DEBUG_SYBORG_INT
32 #ifdef DEBUG_SYBORG_INT
33 #define DPRINTF(fmt, ...) \
34 do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0)
35 #define BADF(fmt, ...) \
36 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \
37 exit(1);} while (0)
38 #else
39 #define DPRINTF(fmt, ...) do {} while(0)
40 #define BADF(fmt, ...) \
41 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0)
42 #endif
43 enum {
44 INT_ID = 0,
45 INT_STATUS = 1, /* number of pending interrupts */
46 INT_CURRENT = 2, /* next interrupt to be serviced */
47 INT_DISABLE_ALL = 3,
48 INT_DISABLE = 4,
49 INT_ENABLE = 5,
50 INT_TOTAL = 6
53 typedef struct {
54 unsigned level:1;
55 unsigned enabled:1;
56 } syborg_int_flags;
58 typedef struct {
59 SysBusDevice busdev;
60 MemoryRegion iomem;
61 int pending_count;
62 uint32_t num_irqs;
63 syborg_int_flags *flags;
64 qemu_irq parent_irq;
65 } SyborgIntState;
67 static void syborg_int_update(SyborgIntState *s)
69 DPRINTF("pending %d\n", s->pending_count);
70 qemu_set_irq(s->parent_irq, s->pending_count > 0);
73 static void syborg_int_set_irq(void *opaque, int irq, int level)
75 SyborgIntState *s = (SyborgIntState *)opaque;
77 if (s->flags[irq].level == level)
78 return;
80 s->flags[irq].level = level;
81 if (s->flags[irq].enabled) {
82 if (level)
83 s->pending_count++;
84 else
85 s->pending_count--;
86 syborg_int_update(s);
90 static uint64_t syborg_int_read(void *opaque, hwaddr offset,
91 unsigned size)
93 SyborgIntState *s = (SyborgIntState *)opaque;
94 int i;
96 offset &= 0xfff;
97 switch (offset >> 2) {
98 case INT_ID:
99 return SYBORG_ID_INT;
100 case INT_STATUS:
101 DPRINTF("read status=%d\n", s->pending_count);
102 return s->pending_count;
104 case INT_CURRENT:
105 for (i = 0; i < s->num_irqs; i++) {
106 if (s->flags[i].level & s->flags[i].enabled) {
107 DPRINTF("read current=%d\n", i);
108 return i;
111 DPRINTF("read current=none\n");
112 return 0xffffffffu;
114 default:
115 cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
116 (int)offset);
117 return 0;
121 static void syborg_int_write(void *opaque, hwaddr offset,
122 uint64_t value, unsigned size)
124 SyborgIntState *s = (SyborgIntState *)opaque;
125 int i;
126 offset &= 0xfff;
128 DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value);
129 switch (offset >> 2) {
130 case INT_DISABLE_ALL:
131 s->pending_count = 0;
132 for (i = 0; i < s->num_irqs; i++)
133 s->flags[i].enabled = 0;
134 break;
136 case INT_DISABLE:
137 if (value >= s->num_irqs)
138 break;
139 if (s->flags[value].enabled) {
140 if (s->flags[value].enabled)
141 s->pending_count--;
142 s->flags[value].enabled = 0;
144 break;
146 case INT_ENABLE:
147 if (value >= s->num_irqs)
148 break;
149 if (!(s->flags[value].enabled)) {
150 if(s->flags[value].level)
151 s->pending_count++;
152 s->flags[value].enabled = 1;
154 break;
156 default:
157 cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
158 (int)offset);
159 return;
161 syborg_int_update(s);
164 static const MemoryRegionOps syborg_int_ops = {
165 .read = syborg_int_read,
166 .write = syborg_int_write,
167 .endianness = DEVICE_NATIVE_ENDIAN,
170 static void syborg_int_save(QEMUFile *f, void *opaque)
172 SyborgIntState *s = (SyborgIntState *)opaque;
173 int i;
175 qemu_put_be32(f, s->num_irqs);
176 qemu_put_be32(f, s->pending_count);
177 for (i = 0; i < s->num_irqs; i++) {
178 qemu_put_be32(f, s->flags[i].enabled
179 | ((unsigned)s->flags[i].level << 1));
183 static int syborg_int_load(QEMUFile *f, void *opaque, int version_id)
185 SyborgIntState *s = (SyborgIntState *)opaque;
186 uint32_t val;
187 int i;
189 if (version_id != 1)
190 return -EINVAL;
192 val = qemu_get_be32(f);
193 if (val != s->num_irqs)
194 return -EINVAL;
195 s->pending_count = qemu_get_be32(f);
196 for (i = 0; i < s->num_irqs; i++) {
197 val = qemu_get_be32(f);
198 s->flags[i].enabled = val & 1;
199 s->flags[i].level = (val >> 1) & 1;
201 return 0;
204 static int syborg_int_init(SysBusDevice *sbd)
206 DeviceState *dev = DEVICE(sbd);
207 SyborgIntState *s = SYBORG_INT(dev);
209 sysbus_init_irq(dev, &s->parent_irq);
210 qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs);
211 memory_region_init_io(&s->iomem, &syborg_int_ops, s,
212 "interrupt", 0x1000);
213 sysbus_init_mmio(sbd, &s->iomem);
214 s->flags = g_malloc0(s->num_irqs * sizeof(syborg_int_flags));
216 register_savevm(&dev->qdev, "syborg_int", -1, 1, syborg_int_save,
217 syborg_int_load, s);
218 return 0;
221 static Property syborg_fb_properties[] = {
222 DEFINE_PROP_UINT32("num-interrupts", SyborgIntState, num_irqs, 64),
223 DEFINE_PROP_END_OF_LIST()
226 static void syborg_int_class_init(ObjectClass *klass, void *data)
228 DeviceClass *dc = DEVICE_CLASS(klass);
229 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
230 dc->props = syborg_fb_properties;
231 k->init = syborg_int_init;
234 static const TypeInfo syborg_int_info = {
235 .name = "syborg,interrupt",
236 .parent = TYPE_SYS_BUS_DEVICE,
237 .instance_size = sizeof(SyborgIntState),
238 .class_init = syborg_int_class_init
241 static void syborg_interrupt_register_types(void)
243 type_register_static(&syborg_int_info);
246 type_init(syborg_interrupt_register_types)