Merge remote-tracking branch 'qemu-project/master'
[qemu/ar7.git] / hw / arm / syborg_interrupt.c
blob4b36efa237418a7f29ff3d1b2422add5ce9be9b4
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 "migration/register.h" /* register_savevm_live */
29 #include "syborg.h"
31 //#define DEBUG_SYBORG_INT
33 #ifdef DEBUG_SYBORG_INT
34 #define DPRINTF(fmt, ...) \
35 do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0)
36 #define BADF(fmt, ...) \
37 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \
38 exit(1);} while (0)
39 #else
40 #define DPRINTF(fmt, ...) do {} while(0)
41 #define BADF(fmt, ...) \
42 do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0)
43 #endif
44 enum {
45 INT_ID = 0,
46 INT_STATUS = 1, /* number of pending interrupts */
47 INT_CURRENT = 2, /* next interrupt to be serviced */
48 INT_DISABLE_ALL = 3,
49 INT_DISABLE = 4,
50 INT_ENABLE = 5,
51 INT_TOTAL = 6
54 typedef struct {
55 unsigned level:1;
56 unsigned enabled:1;
57 } syborg_int_flags;
59 typedef struct {
60 SysBusDevice busdev;
61 MemoryRegion iomem;
62 int pending_count;
63 uint32_t num_irqs;
64 syborg_int_flags *flags;
65 qemu_irq parent_irq;
66 } SyborgIntState;
68 static void syborg_int_update(SyborgIntState *s)
70 DPRINTF("pending %d\n", s->pending_count);
71 qemu_set_irq(s->parent_irq, s->pending_count > 0);
74 static void syborg_int_set_irq(void *opaque, int irq, int level)
76 SyborgIntState *s = (SyborgIntState *)opaque;
78 if (s->flags[irq].level == level)
79 return;
81 s->flags[irq].level = level;
82 if (s->flags[irq].enabled) {
83 if (level)
84 s->pending_count++;
85 else
86 s->pending_count--;
87 syborg_int_update(s);
91 static uint64_t syborg_int_read(void *opaque, hwaddr offset,
92 unsigned size)
94 SyborgIntState *s = (SyborgIntState *)opaque;
95 int i;
97 offset &= 0xfff;
98 switch (offset >> 2) {
99 case INT_ID:
100 return SYBORG_ID_INT;
101 case INT_STATUS:
102 DPRINTF("read status=%d\n", s->pending_count);
103 return s->pending_count;
105 case INT_CURRENT:
106 for (i = 0; i < s->num_irqs; i++) {
107 if (s->flags[i].level & s->flags[i].enabled) {
108 DPRINTF("read current=%d\n", i);
109 return i;
112 DPRINTF("read current=none\n");
113 return 0xffffffffu;
115 default:
116 cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
117 (int)offset);
118 return 0;
122 static void syborg_int_write(void *opaque, hwaddr offset,
123 uint64_t value, unsigned size)
125 SyborgIntState *s = (SyborgIntState *)opaque;
126 int i;
127 offset &= 0xfff;
129 DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value);
130 switch (offset >> 2) {
131 case INT_DISABLE_ALL:
132 s->pending_count = 0;
133 for (i = 0; i < s->num_irqs; i++)
134 s->flags[i].enabled = 0;
135 break;
137 case INT_DISABLE:
138 if (value >= s->num_irqs)
139 break;
140 if (s->flags[value].enabled) {
141 if (s->flags[value].enabled)
142 s->pending_count--;
143 s->flags[value].enabled = 0;
145 break;
147 case INT_ENABLE:
148 if (value >= s->num_irqs)
149 break;
150 if (!(s->flags[value].enabled)) {
151 if(s->flags[value].level)
152 s->pending_count++;
153 s->flags[value].enabled = 1;
155 break;
157 default:
158 cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
159 (int)offset);
160 return;
162 syborg_int_update(s);
165 static const MemoryRegionOps syborg_int_ops = {
166 .read = syborg_int_read,
167 .write = syborg_int_write,
168 .endianness = DEVICE_NATIVE_ENDIAN,
171 static void syborg_int_save(QEMUFile *f, void *opaque)
173 SyborgIntState *s = (SyborgIntState *)opaque;
174 int i;
176 qemu_put_be32(f, s->num_irqs);
177 qemu_put_be32(f, s->pending_count);
178 for (i = 0; i < s->num_irqs; i++) {
179 qemu_put_be32(f, s->flags[i].enabled
180 | ((unsigned)s->flags[i].level << 1));
184 static int syborg_int_load(QEMUFile *f, void *opaque, int version_id)
186 SyborgIntState *s = (SyborgIntState *)opaque;
187 uint32_t val;
188 int i;
190 if (version_id != 1)
191 return -EINVAL;
193 val = qemu_get_be32(f);
194 if (val != s->num_irqs)
195 return -EINVAL;
196 s->pending_count = qemu_get_be32(f);
197 for (i = 0; i < s->num_irqs; i++) {
198 val = qemu_get_be32(f);
199 s->flags[i].enabled = val & 1;
200 s->flags[i].level = (val >> 1) & 1;
202 return 0;
205 static SaveVMHandlers savevm_syborg_int = {
206 .save_state = syborg_int_save,
207 .load_state = syborg_int_load
210 static int syborg_int_init(SysBusDevice *sbd)
212 DeviceState *dev = DEVICE(sbd);
213 SyborgIntState *s = SYBORG_INT(dev);
215 sysbus_init_irq(dev, &s->parent_irq);
216 qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs);
217 memory_region_init_io(&s->iomem, &syborg_int_ops, s,
218 "interrupt", 0x1000);
219 sysbus_init_mmio(sbd, &s->iomem);
220 s->flags = g_malloc0(s->num_irqs * sizeof(syborg_int_flags));
222 register_savevm_live(&dev->qdev, "syborg_int", -1, 1,
223 &savevm_syborg_int, s);
224 return 0;
227 static Property syborg_fb_properties[] = {
228 DEFINE_PROP_UINT32("num-interrupts", SyborgIntState, num_irqs, 64),
229 DEFINE_PROP_END_OF_LIST()
232 static void syborg_int_class_init(ObjectClass *klass, void *data)
234 DeviceClass *dc = DEVICE_CLASS(klass);
235 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
236 dc->props = syborg_fb_properties;
237 k->init = syborg_int_init;
240 static const TypeInfo syborg_int_info = {
241 .name = "syborg,interrupt",
242 .parent = TYPE_SYS_BUS_DEVICE,
243 .instance_size = sizeof(SyborgIntState),
244 .class_init = syborg_int_class_init,
245 .class_size = sizeof(SysBusDeviceClass)
248 static void syborg_interrupt_register_types(void)
250 type_register_static(&syborg_int_info);
253 type_init(syborg_interrupt_register_types)