Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[qemu] / hw / syborg_interrupt.c
1 /*
2  * Syborg interrupt controller.
3  *
4  * Copyright (c) 2008 CodeSourcery
5  *
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  */
24
25 #include "sysbus.h"
26 #include "syborg.h"
27
28 //#define DEBUG_SYBORG_INT
29
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
49 };
50
51 typedef struct {
52     unsigned level:1;
53     unsigned enabled:1;
54 } syborg_int_flags;
55
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;
63
64 static void syborg_int_update(SyborgIntState *s)
65 {
66     DPRINTF("pending %d\n", s->pending_count);
67     qemu_set_irq(s->parent_irq, s->pending_count > 0);
68 }
69
70 static void syborg_int_set_irq(void *opaque, int irq, int level)
71 {
72     SyborgIntState *s = (SyborgIntState *)opaque;
73
74     if (s->flags[irq].level == level)
75         return;
76
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);
84     }
85 }
86
87 static uint32_t syborg_int_read(void *opaque, target_phys_addr_t offset)
88 {
89     SyborgIntState *s = (SyborgIntState *)opaque;
90     int i;
91
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;
99
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;
105             }
106         }
107         DPRINTF("read current=none\n");
108         return 0xffffffffu;
109
110     default:
111         cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
112                   (int)offset);
113         return 0;
114     }
115 }
116
117 static void syborg_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
118 {
119     SyborgIntState *s = (SyborgIntState *)opaque;
120     int i;
121     offset &= 0xfff;
122
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;
130
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;
138         }
139         break;
140
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;
148       }
149       break;
150
151     default:
152         cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
153                   (int)offset);
154         return;
155     }
156     syborg_int_update(s);
157 }
158
159 static CPUReadMemoryFunc *syborg_int_readfn[] = {
160     syborg_int_read,
161     syborg_int_read,
162     syborg_int_read
163 };
164
165 static CPUWriteMemoryFunc *syborg_int_writefn[] = {
166     syborg_int_write,
167     syborg_int_write,
168     syborg_int_write
169 };
170
171 static void syborg_int_save(QEMUFile *f, void *opaque)
172 {
173     SyborgIntState *s = (SyborgIntState *)opaque;
174     int i;
175
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));
181     }
182 }
183
184 static int syborg_int_load(QEMUFile *f, void *opaque, int version_id)
185 {
186     SyborgIntState *s = (SyborgIntState *)opaque;
187     uint32_t val;
188     int i;
189
190     if (version_id != 1)
191         return -EINVAL;
192
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;
201     }
202     return 0;
203 }
204
205 static void syborg_int_init(SysBusDevice *dev)
206 {
207     SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev);
208     int iomemtype;
209
210     sysbus_init_irq(dev, &s->parent_irq);
211     s->num_irqs = qdev_get_prop_int(&dev->qdev, "num-interrupts", 64);
212     qdev_init_gpio_in(&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));
217
218     register_savevm("syborg_int", -1, 1, syborg_int_save, syborg_int_load, s);
219 }
220
221 static void syborg_interrupt_register_devices(void)
222 {
223     sysbus_register_dev("syborg,interrupt", sizeof(SyborgIntState),
224                         syborg_int_init);
225 }
226
227 device_init(syborg_interrupt_register_devices)