]>
Commit | Line | Data |
---|---|---|
ee76f82e BS |
1 | /* |
2 | * QEMU Sparc Sun4c interrupt controller emulation | |
3 | * | |
4 | * Based on slavio_intctl, copyright (c) 2003-2005 Fabrice Bellard | |
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 | */ | |
e32cba29 | 24 | |
ee76f82e BS |
25 | #include "hw.h" |
26 | #include "sun4m.h" | |
376253ec | 27 | #include "monitor.h" |
e32cba29 BS |
28 | #include "sysbus.h" |
29 | ||
ee76f82e BS |
30 | //#define DEBUG_IRQ_COUNT |
31 | //#define DEBUG_IRQ | |
32 | ||
33 | #ifdef DEBUG_IRQ | |
001faf32 BS |
34 | #define DPRINTF(fmt, ...) \ |
35 | do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0) | |
ee76f82e | 36 | #else |
001faf32 | 37 | #define DPRINTF(fmt, ...) |
ee76f82e BS |
38 | #endif |
39 | ||
40 | /* | |
41 | * Registers of interrupt controller in sun4c. | |
42 | * | |
43 | */ | |
44 | ||
45 | #define MAX_PILS 16 | |
46 | ||
47 | typedef struct Sun4c_INTCTLState { | |
e32cba29 | 48 | SysBusDevice busdev; |
1ce2c9cd | 49 | MemoryRegion iomem; |
ee76f82e BS |
50 | #ifdef DEBUG_IRQ_COUNT |
51 | uint64_t irq_count; | |
52 | #endif | |
e32cba29 | 53 | qemu_irq cpu_irqs[MAX_PILS]; |
ee76f82e BS |
54 | const uint32_t *intbit_to_level; |
55 | uint32_t pil_out; | |
56 | uint8_t reg; | |
57 | uint8_t pending; | |
58 | } Sun4c_INTCTLState; | |
59 | ||
e64d7d59 | 60 | #define INTCTL_SIZE 1 |
ee76f82e BS |
61 | |
62 | static void sun4c_check_interrupts(void *opaque); | |
63 | ||
1ce2c9cd AK |
64 | static uint64_t sun4c_intctl_mem_read(void *opaque, target_phys_addr_t addr, |
65 | unsigned size) | |
ee76f82e BS |
66 | { |
67 | Sun4c_INTCTLState *s = opaque; | |
68 | uint32_t ret; | |
69 | ||
70 | ret = s->reg; | |
71 | DPRINTF("read reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); | |
72 | ||
73 | return ret; | |
74 | } | |
75 | ||
1ce2c9cd AK |
76 | static void sun4c_intctl_mem_write(void *opaque, target_phys_addr_t addr, |
77 | uint64_t val, unsigned size) | |
ee76f82e BS |
78 | { |
79 | Sun4c_INTCTLState *s = opaque; | |
80 | ||
1ce2c9cd | 81 | DPRINTF("write reg 0x" TARGET_FMT_plx " = %x\n", addr, (unsigned)val); |
ee76f82e BS |
82 | val &= 0xbf; |
83 | s->reg = val; | |
84 | sun4c_check_interrupts(s); | |
85 | } | |
86 | ||
1ce2c9cd AK |
87 | static const MemoryRegionOps sun4c_intctl_mem_ops = { |
88 | .read = sun4c_intctl_mem_read, | |
89 | .write = sun4c_intctl_mem_write, | |
90 | .endianness = DEVICE_NATIVE_ENDIAN, | |
91 | .valid = { | |
92 | .min_access_size = 1, | |
93 | .max_access_size = 1, | |
94 | }, | |
ee76f82e BS |
95 | }; |
96 | ||
376253ec | 97 | void sun4c_pic_info(Monitor *mon, void *opaque) |
ee76f82e BS |
98 | { |
99 | Sun4c_INTCTLState *s = opaque; | |
100 | ||
376253ec AL |
101 | monitor_printf(mon, "master: pending 0x%2.2x, enabled 0x%2.2x\n", |
102 | s->pending, s->reg); | |
ee76f82e BS |
103 | } |
104 | ||
376253ec | 105 | void sun4c_irq_info(Monitor *mon, void *opaque) |
ee76f82e BS |
106 | { |
107 | #ifndef DEBUG_IRQ_COUNT | |
376253ec | 108 | monitor_printf(mon, "irq statistic code not compiled.\n"); |
ee76f82e BS |
109 | #else |
110 | Sun4c_INTCTLState *s = opaque; | |
111 | int64_t count; | |
112 | ||
376253ec | 113 | monitor_printf(mon, "IRQ statistics:\n"); |
0bf9e31a | 114 | count = s->irq_count; |
ee76f82e | 115 | if (count > 0) |
0bf9e31a | 116 | monitor_printf(mon, " %" PRId64 "\n", count); |
ee76f82e BS |
117 | #endif |
118 | } | |
119 | ||
120 | static const uint32_t intbit_to_level[] = { 0, 1, 4, 6, 8, 10, 0, 14, }; | |
121 | ||
122 | static void sun4c_check_interrupts(void *opaque) | |
123 | { | |
124 | Sun4c_INTCTLState *s = opaque; | |
125 | uint32_t pil_pending; | |
126 | unsigned int i; | |
127 | ||
ee76f82e BS |
128 | pil_pending = 0; |
129 | if (s->pending && !(s->reg & 0x80000000)) { | |
130 | for (i = 0; i < 8; i++) { | |
131 | if (s->pending & (1 << i)) | |
132 | pil_pending |= 1 << intbit_to_level[i]; | |
133 | } | |
134 | } | |
135 | ||
136 | for (i = 0; i < MAX_PILS; i++) { | |
137 | if (pil_pending & (1 << i)) { | |
138 | if (!(s->pil_out & (1 << i))) | |
139 | qemu_irq_raise(s->cpu_irqs[i]); | |
140 | } else { | |
141 | if (s->pil_out & (1 << i)) | |
142 | qemu_irq_lower(s->cpu_irqs[i]); | |
143 | } | |
144 | } | |
145 | s->pil_out = pil_pending; | |
146 | } | |
147 | ||
148 | /* | |
149 | * "irq" here is the bit number in the system interrupt register | |
150 | */ | |
151 | static void sun4c_set_irq(void *opaque, int irq, int level) | |
152 | { | |
153 | Sun4c_INTCTLState *s = opaque; | |
154 | uint32_t mask = 1 << irq; | |
155 | uint32_t pil = intbit_to_level[irq]; | |
156 | ||
157 | DPRINTF("Set irq %d -> pil %d level %d\n", irq, pil, | |
158 | level); | |
159 | if (pil > 0) { | |
160 | if (level) { | |
161 | #ifdef DEBUG_IRQ_COUNT | |
0bf9e31a | 162 | s->irq_count++; |
ee76f82e BS |
163 | #endif |
164 | s->pending |= mask; | |
165 | } else { | |
166 | s->pending &= ~mask; | |
167 | } | |
168 | sun4c_check_interrupts(s); | |
169 | } | |
170 | } | |
171 | ||
9902571d BS |
172 | static const VMStateDescription vmstate_sun4c_intctl = { |
173 | .name ="sun4c_intctl", | |
174 | .version_id = 1, | |
175 | .minimum_version_id = 1, | |
176 | .minimum_version_id_old = 1, | |
177 | .fields = (VMStateField []) { | |
178 | VMSTATE_UINT8(reg, Sun4c_INTCTLState), | |
179 | VMSTATE_UINT8(pending, Sun4c_INTCTLState), | |
180 | VMSTATE_END_OF_LIST() | |
181 | } | |
182 | }; | |
ee76f82e | 183 | |
9a2070d3 | 184 | static void sun4c_intctl_reset(DeviceState *d) |
ee76f82e | 185 | { |
9a2070d3 | 186 | Sun4c_INTCTLState *s = container_of(d, Sun4c_INTCTLState, busdev.qdev); |
ee76f82e BS |
187 | |
188 | s->reg = 1; | |
189 | s->pending = 0; | |
ee76f82e BS |
190 | } |
191 | ||
81a322d4 | 192 | static int sun4c_intctl_init1(SysBusDevice *dev) |
e32cba29 BS |
193 | { |
194 | Sun4c_INTCTLState *s = FROM_SYSBUS(Sun4c_INTCTLState, dev); | |
e32cba29 | 195 | unsigned int i; |
ee76f82e | 196 | |
1ce2c9cd AK |
197 | memory_region_init_io(&s->iomem, &sun4c_intctl_mem_ops, s, |
198 | "intctl", INTCTL_SIZE); | |
750ecd44 | 199 | sysbus_init_mmio(dev, &s->iomem); |
e32cba29 BS |
200 | qdev_init_gpio_in(&dev->qdev, sun4c_set_irq, 8); |
201 | ||
202 | for (i = 0; i < MAX_PILS; i++) { | |
203 | sysbus_init_irq(dev, &s->cpu_irqs[i]); | |
204 | } | |
9a2070d3 | 205 | |
81a322d4 | 206 | return 0; |
ee76f82e | 207 | } |
e32cba29 | 208 | |
999e12bb AL |
209 | static void sun4c_intctl_class_init(ObjectClass *klass, void *data) |
210 | { | |
39bffca2 | 211 | DeviceClass *dc = DEVICE_CLASS(klass); |
999e12bb AL |
212 | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
213 | ||
214 | k->init = sun4c_intctl_init1; | |
39bffca2 AL |
215 | dc->reset = sun4c_intctl_reset; |
216 | dc->vmsd = &vmstate_sun4c_intctl; | |
999e12bb AL |
217 | } |
218 | ||
39bffca2 AL |
219 | static TypeInfo sun4c_intctl_info = { |
220 | .name = "sun4c_intctl", | |
221 | .parent = TYPE_SYS_BUS_DEVICE, | |
222 | .instance_size = sizeof(Sun4c_INTCTLState), | |
223 | .class_init = sun4c_intctl_class_init, | |
e32cba29 BS |
224 | }; |
225 | ||
226 | static void sun4c_intctl_register_devices(void) | |
227 | { | |
39bffca2 | 228 | type_register_static(&sun4c_intctl_info); |
e32cba29 BS |
229 | } |
230 | ||
231 | device_init(sun4c_intctl_register_devices) |