]> git.proxmox.com Git - mirror_qemu.git/blame - hw/intc/aspeed_vic.c
i386: Add x-force-features option for testing
[mirror_qemu.git] / hw / intc / aspeed_vic.c
CommitLineData
0c69996e
AJ
1/*
2 * ASPEED Interrupt Controller (New)
3 *
4 * Andrew Jeffery <andrew@aj.id.au>
5 *
6 * Copyright 2015, 2016 IBM Corp.
7 *
8 * This code is licensed under the GPL version 2 or later. See
9 * the COPYING file in the top-level directory.
10 */
11
12/* The hardware exposes two register sets, a legacy set and a 'new' set. The
13 * model implements the 'new' register set, and logs warnings on accesses to
14 * the legacy IO space.
15 *
16 * The hardware uses 32bit registers to manage 51 IRQs, with low and high
17 * registers for each conceptual register. The device model's implementation
18 * uses 64bit data types to store both low and high register values (in the one
19 * member), but must cope with access offset values in multiples of 4 passed to
20 * the callbacks. As such the read() and write() implementations process the
21 * provided offset to understand whether the access is requesting the lower or
22 * upper 32 bits of the 64bit member.
23 *
24 * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
25 * fields have separate "enable"/"status" and "clear" registers, where set bits
26 * are written to one or the other to change state (avoiding a
27 * read-modify-write sequence).
28 */
29
30#include "qemu/osdep.h"
0c69996e
AJ
31#include "hw/intc/aspeed_vic.h"
32#include "qemu/bitops.h"
22b31af2 33#include "qemu/log.h"
0b8fa32f 34#include "qemu/module.h"
0c69996e
AJ
35#include "trace.h"
36
37#define AVIC_NEW_BASE_OFFSET 0x80
38
39#define AVIC_L_MASK 0xFFFFFFFFU
40#define AVIC_H_MASK 0x0007FFFFU
41#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
42
43static void aspeed_vic_update(AspeedVICState *s)
44{
45 uint64_t new = (s->raw & s->enable);
46 uint64_t flags;
47
48 flags = new & s->select;
49 trace_aspeed_vic_update_fiq(!!flags);
50 qemu_set_irq(s->fiq, !!flags);
51
52 flags = new & ~s->select;
53 trace_aspeed_vic_update_irq(!!flags);
54 qemu_set_irq(s->irq, !!flags);
55}
56
57static void aspeed_vic_set_irq(void *opaque, int irq, int level)
58{
59 uint64_t irq_mask;
60 bool raise;
61 AspeedVICState *s = (AspeedVICState *)opaque;
62
63 if (irq > ASPEED_VIC_NR_IRQS) {
64 qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
65 __func__, irq);
66 return;
67 }
68
69 trace_aspeed_vic_set_irq(irq, level);
70
71 irq_mask = BIT(irq);
72 if (s->sense & irq_mask) {
73 /* level-triggered */
74 if (s->event & irq_mask) {
75 /* high-sensitive */
76 raise = level;
77 } else {
78 /* low-sensitive */
79 raise = !level;
80 }
81 s->raw = deposit64(s->raw, irq, 1, raise);
82 } else {
83 uint64_t old_level = s->level & irq_mask;
84
85 /* edge-triggered */
86 if (s->dual_edge & irq_mask) {
87 raise = (!!old_level) != (!!level);
88 } else {
89 if (s->event & irq_mask) {
90 /* rising-sensitive */
91 raise = !old_level && level;
92 } else {
93 /* falling-sensitive */
94 raise = old_level && !level;
95 }
96 }
97 if (raise) {
98 s->raw = deposit64(s->raw, irq, 1, raise);
99 }
100 }
101 s->level = deposit64(s->level, irq, 1, level);
102 aspeed_vic_update(s);
103}
104
105static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
106{
0c69996e 107 AspeedVICState *s = (AspeedVICState *)opaque;
ebd205c0
AJ
108 hwaddr n_offset;
109 uint64_t val;
110 bool high;
0c69996e
AJ
111
112 if (offset < AVIC_NEW_BASE_OFFSET) {
ebd205c0
AJ
113 high = false;
114 n_offset = offset;
115 } else {
116 high = !!(offset & 0x4);
117 n_offset = (offset & ~0x4);
0c69996e
AJ
118 }
119
0c69996e 120 switch (n_offset) {
ebd205c0
AJ
121 case 0x80: /* IRQ Status */
122 case 0x00:
0c69996e
AJ
123 val = s->raw & ~s->select & s->enable;
124 break;
ebd205c0
AJ
125 case 0x88: /* FIQ Status */
126 case 0x04:
0c69996e
AJ
127 val = s->raw & s->select & s->enable;
128 break;
ebd205c0
AJ
129 case 0x90: /* Raw Interrupt Status */
130 case 0x08:
0c69996e
AJ
131 val = s->raw;
132 break;
ebd205c0
AJ
133 case 0x98: /* Interrupt Selection */
134 case 0x0c:
0c69996e
AJ
135 val = s->select;
136 break;
ebd205c0
AJ
137 case 0xa0: /* Interrupt Enable */
138 case 0x10:
0c69996e
AJ
139 val = s->enable;
140 break;
ebd205c0
AJ
141 case 0xb0: /* Software Interrupt */
142 case 0x18:
0c69996e
AJ
143 val = s->trigger;
144 break;
ebd205c0
AJ
145 case 0xc0: /* Interrupt Sensitivity */
146 case 0x24:
0c69996e
AJ
147 val = s->sense;
148 break;
ebd205c0
AJ
149 case 0xc8: /* Interrupt Both Edge Trigger Control */
150 case 0x28:
0c69996e
AJ
151 val = s->dual_edge;
152 break;
ebd205c0
AJ
153 case 0xd0: /* Interrupt Event */
154 case 0x2c:
0c69996e
AJ
155 val = s->event;
156 break;
ebd205c0 157 case 0xe0: /* Edge Triggered Interrupt Status */
0c69996e
AJ
158 val = s->raw & ~s->sense;
159 break;
160 /* Illegal */
ebd205c0
AJ
161 case 0xa8: /* Interrupt Enable Clear */
162 case 0xb8: /* Software Interrupt Clear */
163 case 0xd8: /* Edge Triggered Interrupt Clear */
0c69996e
AJ
164 qemu_log_mask(LOG_GUEST_ERROR,
165 "%s: Read of write-only register with offset 0x%"
166 HWADDR_PRIx "\n", __func__, offset);
167 val = 0;
168 break;
169 default:
170 qemu_log_mask(LOG_GUEST_ERROR,
171 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
172 __func__, offset);
173 val = 0;
174 break;
175 }
176 if (high) {
177 val = extract64(val, 32, 19);
ebd205c0
AJ
178 } else {
179 val = extract64(val, 0, 32);
0c69996e
AJ
180 }
181 trace_aspeed_vic_read(offset, size, val);
182 return val;
183}
184
185static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
186 unsigned size)
187{
0c69996e 188 AspeedVICState *s = (AspeedVICState *)opaque;
ebd205c0
AJ
189 hwaddr n_offset;
190 bool high;
0c69996e
AJ
191
192 if (offset < AVIC_NEW_BASE_OFFSET) {
ebd205c0
AJ
193 high = false;
194 n_offset = offset;
195 } else {
196 high = !!(offset & 0x4);
197 n_offset = (offset & ~0x4);
0c69996e
AJ
198 }
199
0c69996e
AJ
200 trace_aspeed_vic_write(offset, size, data);
201
202 /* Given we have members using separate enable/clear registers, deposit64()
203 * isn't quite the tool for the job. Instead, relocate the incoming bits to
204 * the required bit offset based on the provided access address
205 */
206 if (high) {
207 data &= AVIC_H_MASK;
208 data <<= 32;
209 } else {
210 data &= AVIC_L_MASK;
211 }
212
213 switch (n_offset) {
ebd205c0
AJ
214 case 0x98: /* Interrupt Selection */
215 case 0x0c:
0c69996e
AJ
216 /* Register has deposit64() semantics - overwrite requested 32 bits */
217 if (high) {
218 s->select &= AVIC_L_MASK;
219 } else {
220 s->select &= ((uint64_t) AVIC_H_MASK) << 32;
221 }
222 s->select |= data;
223 break;
ebd205c0
AJ
224 case 0xa0: /* Interrupt Enable */
225 case 0x10:
0c69996e
AJ
226 s->enable |= data;
227 break;
ebd205c0
AJ
228 case 0xa8: /* Interrupt Enable Clear */
229 case 0x14:
0c69996e
AJ
230 s->enable &= ~data;
231 break;
ebd205c0
AJ
232 case 0xb0: /* Software Interrupt */
233 case 0x18:
0c69996e
AJ
234 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
235 "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
236 break;
ebd205c0
AJ
237 case 0xb8: /* Software Interrupt Clear */
238 case 0x1c:
0c69996e
AJ
239 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
240 "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
241 break;
ebd205c0 242 case 0xd0: /* Interrupt Event */
0c69996e
AJ
243 /* Register has deposit64() semantics - overwrite the top four valid
244 * IRQ bits, as only the top four IRQs (GPIOs) can change their event
245 * type */
246 if (high) {
247 s->event &= ~AVIC_EVENT_W_MASK;
248 s->event |= (data & AVIC_EVENT_W_MASK);
249 } else {
250 qemu_log_mask(LOG_GUEST_ERROR,
251 "Ignoring invalid write to interrupt event register");
252 }
253 break;
ebd205c0
AJ
254 case 0xd8: /* Edge Triggered Interrupt Clear */
255 case 0x38:
0c69996e
AJ
256 s->raw &= ~(data & ~s->sense);
257 break;
ebd205c0
AJ
258 case 0x80: /* IRQ Status */
259 case 0x00:
260 case 0x88: /* FIQ Status */
261 case 0x04:
262 case 0x90: /* Raw Interrupt Status */
263 case 0x08:
264 case 0xc0: /* Interrupt Sensitivity */
265 case 0x24:
266 case 0xc8: /* Interrupt Both Edge Trigger Control */
267 case 0x28:
268 case 0xe0: /* Edge Triggered Interrupt Status */
0c69996e
AJ
269 qemu_log_mask(LOG_GUEST_ERROR,
270 "%s: Write of read-only register with offset 0x%"
271 HWADDR_PRIx "\n", __func__, offset);
272 break;
273
274 default:
275 qemu_log_mask(LOG_GUEST_ERROR,
276 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
277 __func__, offset);
278 break;
279 }
280 aspeed_vic_update(s);
281}
282
283static const MemoryRegionOps aspeed_vic_ops = {
284 .read = aspeed_vic_read,
285 .write = aspeed_vic_write,
286 .endianness = DEVICE_LITTLE_ENDIAN,
287 .valid.min_access_size = 4,
288 .valid.max_access_size = 4,
289 .valid.unaligned = false,
290};
291
292static void aspeed_vic_reset(DeviceState *dev)
293{
294 AspeedVICState *s = ASPEED_VIC(dev);
295
296 s->level = 0;
297 s->raw = 0;
298 s->select = 0;
299 s->enable = 0;
300 s->trigger = 0;
301 s->sense = 0x1F07FFF8FFFFULL;
302 s->dual_edge = 0xF800070000ULL;
303 s->event = 0x5F07FFF8FFFFULL;
304}
305
306#define AVIC_IO_REGION_SIZE 0x20000
307
308static void aspeed_vic_realize(DeviceState *dev, Error **errp)
309{
310 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
311 AspeedVICState *s = ASPEED_VIC(dev);
312
313 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
314 TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
315
316 sysbus_init_mmio(sbd, &s->iomem);
317
318 qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
319 sysbus_init_irq(sbd, &s->irq);
320 sysbus_init_irq(sbd, &s->fiq);
321}
322
323static const VMStateDescription vmstate_aspeed_vic = {
324 .name = "aspeed.new-vic",
325 .version_id = 1,
326 .minimum_version_id = 1,
327 .fields = (VMStateField[]) {
328 VMSTATE_UINT64(level, AspeedVICState),
329 VMSTATE_UINT64(raw, AspeedVICState),
330 VMSTATE_UINT64(select, AspeedVICState),
331 VMSTATE_UINT64(enable, AspeedVICState),
332 VMSTATE_UINT64(trigger, AspeedVICState),
333 VMSTATE_UINT64(sense, AspeedVICState),
334 VMSTATE_UINT64(dual_edge, AspeedVICState),
335 VMSTATE_UINT64(event, AspeedVICState),
336 VMSTATE_END_OF_LIST()
337 }
338};
339
340static void aspeed_vic_class_init(ObjectClass *klass, void *data)
341{
342 DeviceClass *dc = DEVICE_CLASS(klass);
343 dc->realize = aspeed_vic_realize;
344 dc->reset = aspeed_vic_reset;
345 dc->desc = "ASPEED Interrupt Controller (New)";
346 dc->vmsd = &vmstate_aspeed_vic;
347}
348
349static const TypeInfo aspeed_vic_info = {
350 .name = TYPE_ASPEED_VIC,
351 .parent = TYPE_SYS_BUS_DEVICE,
352 .instance_size = sizeof(AspeedVICState),
353 .class_init = aspeed_vic_class_init,
354};
355
356static void aspeed_vic_register_types(void)
357{
358 type_register_static(&aspeed_vic_info);
359}
360
361type_init(aspeed_vic_register_types);