MINI2440: General update
[qemu/mini2440.git] / hw / syborg_interrupt.c
blobe3fbbf7bf37957b99303bc208d311b2ffdabec54
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 int pending_count;
59 int num_irqs;
60 syborg_int_flags *flags;
61 qemu_irq parent_irq;
62 } SyborgIntState;
64 static void syborg_int_update(SyborgIntState *s)
66 DPRINTF("pending %d\n", s->pending_count);
67 qemu_set_irq(s->parent_irq, s->pending_count > 0);
70 static void syborg_int_set_irq(void *opaque, int irq, int level)
72 SyborgIntState *s = (SyborgIntState *)opaque;
74 if (s->flags[irq].level == level)
75 return;
77 s->flags[irq].level = level;
78 if (s->flags[irq].enabled) {
79 if (level)
80 s->pending_count++;
81 else
82 s->pending_count--;
83 syborg_int_update(s);
87 static uint32_t syborg_int_read(void *opaque, target_phys_addr_t offset)
89 SyborgIntState *s = (SyborgIntState *)opaque;
90 int i;
92 offset &= 0xfff;
93 switch (offset >> 2) {
94 case INT_ID:
95 return SYBORG_ID_INT;
96 case INT_STATUS:
97 DPRINTF("read status=%d\n", s->pending_count);
98 return s->pending_count;
100 case INT_CURRENT:
101 for (i = 0; i < s->num_irqs; i++) {
102 if (s->flags[i].level & s->flags[i].enabled) {
103 DPRINTF("read current=%d\n", i);
104 return i;
107 DPRINTF("read current=none\n");
108 return 0xffffffffu;
110 default:
111 cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
112 (int)offset);
113 return 0;
117 static void syborg_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
119 SyborgIntState *s = (SyborgIntState *)opaque;
120 int i;
121 offset &= 0xfff;
123 DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value);
124 switch (offset >> 2) {
125 case INT_DISABLE_ALL:
126 s->pending_count = 0;
127 for (i = 0; i < s->num_irqs; i++)
128 s->flags[i].enabled = 0;
129 break;
131 case INT_DISABLE:
132 if (value >= s->num_irqs)
133 break;
134 if (s->flags[value].enabled) {
135 if (s->flags[value].enabled)
136 s->pending_count--;
137 s->flags[value].enabled = 0;
139 break;
141 case INT_ENABLE:
142 if (value >= s->num_irqs)
143 break;
144 if (!(s->flags[value].enabled)) {
145 if(s->flags[value].level)
146 s->pending_count++;
147 s->flags[value].enabled = 1;
149 break;
151 default:
152 cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
153 (int)offset);
154 return;
156 syborg_int_update(s);
159 static CPUReadMemoryFunc *syborg_int_readfn[] = {
160 syborg_int_read,
161 syborg_int_read,
162 syborg_int_read
165 static CPUWriteMemoryFunc *syborg_int_writefn[] = {
166 syborg_int_write,
167 syborg_int_write,
168 syborg_int_write
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 void syborg_int_init(SysBusDevice *dev)
207 SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev);
208 int iomemtype;
210 sysbus_init_irq(dev, &s->parent_irq);
211 s->num_irqs = qdev_get_prop_int(&dev->qdev, "num-interrupts", 64);
212 qdev_init_irq_sink(&dev->qdev, syborg_int_set_irq, s->num_irqs);
213 iomemtype = cpu_register_io_memory(0, syborg_int_readfn,
214 syborg_int_writefn, s);
215 sysbus_init_mmio(dev, 0x1000, iomemtype);
216 s->flags = qemu_mallocz(s->num_irqs * sizeof(syborg_int_flags));
218 register_savevm("syborg_int", -1, 1, syborg_int_save, syborg_int_load, s);
221 static void syborg_interrupt_register_devices(void)
223 sysbus_register_dev("syborg,interrupt", sizeof(SyborgIntState),
224 syborg_int_init);
227 device_init(syborg_interrupt_register_devices)