]> git.proxmox.com Git - mirror_qemu.git/blame - hw/intc/aspeed_vic.c
Merge remote-tracking branch 'remotes/juanquintela/tags/migration-pull-request' into...
[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"
0c69996e
AJ
34#include "trace.h"
35
36#define AVIC_NEW_BASE_OFFSET 0x80
37
38#define AVIC_L_MASK 0xFFFFFFFFU
39#define AVIC_H_MASK 0x0007FFFFU
40#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
41
42static void aspeed_vic_update(AspeedVICState *s)
43{
44 uint64_t new = (s->raw & s->enable);
45 uint64_t flags;
46
47 flags = new & s->select;
48 trace_aspeed_vic_update_fiq(!!flags);
49 qemu_set_irq(s->fiq, !!flags);
50
51 flags = new & ~s->select;
52 trace_aspeed_vic_update_irq(!!flags);
53 qemu_set_irq(s->irq, !!flags);
54}
55
56static void aspeed_vic_set_irq(void *opaque, int irq, int level)
57{
58 uint64_t irq_mask;
59 bool raise;
60 AspeedVICState *s = (AspeedVICState *)opaque;
61
62 if (irq > ASPEED_VIC_NR_IRQS) {
63 qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
64 __func__, irq);
65 return;
66 }
67
68 trace_aspeed_vic_set_irq(irq, level);
69
70 irq_mask = BIT(irq);
71 if (s->sense & irq_mask) {
72 /* level-triggered */
73 if (s->event & irq_mask) {
74 /* high-sensitive */
75 raise = level;
76 } else {
77 /* low-sensitive */
78 raise = !level;
79 }
80 s->raw = deposit64(s->raw, irq, 1, raise);
81 } else {
82 uint64_t old_level = s->level & irq_mask;
83
84 /* edge-triggered */
85 if (s->dual_edge & irq_mask) {
86 raise = (!!old_level) != (!!level);
87 } else {
88 if (s->event & irq_mask) {
89 /* rising-sensitive */
90 raise = !old_level && level;
91 } else {
92 /* falling-sensitive */
93 raise = old_level && !level;
94 }
95 }
96 if (raise) {
97 s->raw = deposit64(s->raw, irq, 1, raise);
98 }
99 }
100 s->level = deposit64(s->level, irq, 1, level);
101 aspeed_vic_update(s);
102}
103
104static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
105{
106 uint64_t val;
107 const bool high = !!(offset & 0x4);
108 hwaddr n_offset = (offset & ~0x4);
109 AspeedVICState *s = (AspeedVICState *)opaque;
110
111 if (offset < AVIC_NEW_BASE_OFFSET) {
112 qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
113 "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
114 return 0;
115 }
116
117 n_offset -= AVIC_NEW_BASE_OFFSET;
118
119 switch (n_offset) {
120 case 0x0: /* IRQ Status */
121 val = s->raw & ~s->select & s->enable;
122 break;
123 case 0x08: /* FIQ Status */
124 val = s->raw & s->select & s->enable;
125 break;
126 case 0x10: /* Raw Interrupt Status */
127 val = s->raw;
128 break;
129 case 0x18: /* Interrupt Selection */
130 val = s->select;
131 break;
132 case 0x20: /* Interrupt Enable */
133 val = s->enable;
134 break;
135 case 0x30: /* Software Interrupt */
136 val = s->trigger;
137 break;
138 case 0x40: /* Interrupt Sensitivity */
139 val = s->sense;
140 break;
141 case 0x48: /* Interrupt Both Edge Trigger Control */
142 val = s->dual_edge;
143 break;
144 case 0x50: /* Interrupt Event */
145 val = s->event;
146 break;
147 case 0x60: /* Edge Triggered Interrupt Status */
148 val = s->raw & ~s->sense;
149 break;
150 /* Illegal */
151 case 0x28: /* Interrupt Enable Clear */
152 case 0x38: /* Software Interrupt Clear */
153 case 0x58: /* Edge Triggered Interrupt Clear */
154 qemu_log_mask(LOG_GUEST_ERROR,
155 "%s: Read of write-only register with offset 0x%"
156 HWADDR_PRIx "\n", __func__, offset);
157 val = 0;
158 break;
159 default:
160 qemu_log_mask(LOG_GUEST_ERROR,
161 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
162 __func__, offset);
163 val = 0;
164 break;
165 }
166 if (high) {
167 val = extract64(val, 32, 19);
168 }
169 trace_aspeed_vic_read(offset, size, val);
170 return val;
171}
172
173static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
174 unsigned size)
175{
176 const bool high = !!(offset & 0x4);
177 hwaddr n_offset = (offset & ~0x4);
178 AspeedVICState *s = (AspeedVICState *)opaque;
179
180 if (offset < AVIC_NEW_BASE_OFFSET) {
181 qemu_log_mask(LOG_UNIMP,
182 "%s: Ignoring write to legacy registers at 0x%"
183 HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
184 size, data);
185 return;
186 }
187
188 n_offset -= AVIC_NEW_BASE_OFFSET;
189 trace_aspeed_vic_write(offset, size, data);
190
191 /* Given we have members using separate enable/clear registers, deposit64()
192 * isn't quite the tool for the job. Instead, relocate the incoming bits to
193 * the required bit offset based on the provided access address
194 */
195 if (high) {
196 data &= AVIC_H_MASK;
197 data <<= 32;
198 } else {
199 data &= AVIC_L_MASK;
200 }
201
202 switch (n_offset) {
203 case 0x18: /* Interrupt Selection */
204 /* Register has deposit64() semantics - overwrite requested 32 bits */
205 if (high) {
206 s->select &= AVIC_L_MASK;
207 } else {
208 s->select &= ((uint64_t) AVIC_H_MASK) << 32;
209 }
210 s->select |= data;
211 break;
212 case 0x20: /* Interrupt Enable */
213 s->enable |= data;
214 break;
215 case 0x28: /* Interrupt Enable Clear */
216 s->enable &= ~data;
217 break;
218 case 0x30: /* Software Interrupt */
219 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
220 "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
221 break;
222 case 0x38: /* Software Interrupt Clear */
223 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
224 "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
225 break;
226 case 0x50: /* Interrupt Event */
227 /* Register has deposit64() semantics - overwrite the top four valid
228 * IRQ bits, as only the top four IRQs (GPIOs) can change their event
229 * type */
230 if (high) {
231 s->event &= ~AVIC_EVENT_W_MASK;
232 s->event |= (data & AVIC_EVENT_W_MASK);
233 } else {
234 qemu_log_mask(LOG_GUEST_ERROR,
235 "Ignoring invalid write to interrupt event register");
236 }
237 break;
238 case 0x58: /* Edge Triggered Interrupt Clear */
239 s->raw &= ~(data & ~s->sense);
240 break;
241 case 0x00: /* IRQ Status */
242 case 0x08: /* FIQ Status */
243 case 0x10: /* Raw Interrupt Status */
244 case 0x40: /* Interrupt Sensitivity */
245 case 0x48: /* Interrupt Both Edge Trigger Control */
246 case 0x60: /* Edge Triggered Interrupt Status */
247 qemu_log_mask(LOG_GUEST_ERROR,
248 "%s: Write of read-only register with offset 0x%"
249 HWADDR_PRIx "\n", __func__, offset);
250 break;
251
252 default:
253 qemu_log_mask(LOG_GUEST_ERROR,
254 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
255 __func__, offset);
256 break;
257 }
258 aspeed_vic_update(s);
259}
260
261static const MemoryRegionOps aspeed_vic_ops = {
262 .read = aspeed_vic_read,
263 .write = aspeed_vic_write,
264 .endianness = DEVICE_LITTLE_ENDIAN,
265 .valid.min_access_size = 4,
266 .valid.max_access_size = 4,
267 .valid.unaligned = false,
268};
269
270static void aspeed_vic_reset(DeviceState *dev)
271{
272 AspeedVICState *s = ASPEED_VIC(dev);
273
274 s->level = 0;
275 s->raw = 0;
276 s->select = 0;
277 s->enable = 0;
278 s->trigger = 0;
279 s->sense = 0x1F07FFF8FFFFULL;
280 s->dual_edge = 0xF800070000ULL;
281 s->event = 0x5F07FFF8FFFFULL;
282}
283
284#define AVIC_IO_REGION_SIZE 0x20000
285
286static void aspeed_vic_realize(DeviceState *dev, Error **errp)
287{
288 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
289 AspeedVICState *s = ASPEED_VIC(dev);
290
291 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
292 TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
293
294 sysbus_init_mmio(sbd, &s->iomem);
295
296 qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
297 sysbus_init_irq(sbd, &s->irq);
298 sysbus_init_irq(sbd, &s->fiq);
299}
300
301static const VMStateDescription vmstate_aspeed_vic = {
302 .name = "aspeed.new-vic",
303 .version_id = 1,
304 .minimum_version_id = 1,
305 .fields = (VMStateField[]) {
306 VMSTATE_UINT64(level, AspeedVICState),
307 VMSTATE_UINT64(raw, AspeedVICState),
308 VMSTATE_UINT64(select, AspeedVICState),
309 VMSTATE_UINT64(enable, AspeedVICState),
310 VMSTATE_UINT64(trigger, AspeedVICState),
311 VMSTATE_UINT64(sense, AspeedVICState),
312 VMSTATE_UINT64(dual_edge, AspeedVICState),
313 VMSTATE_UINT64(event, AspeedVICState),
314 VMSTATE_END_OF_LIST()
315 }
316};
317
318static void aspeed_vic_class_init(ObjectClass *klass, void *data)
319{
320 DeviceClass *dc = DEVICE_CLASS(klass);
321 dc->realize = aspeed_vic_realize;
322 dc->reset = aspeed_vic_reset;
323 dc->desc = "ASPEED Interrupt Controller (New)";
324 dc->vmsd = &vmstate_aspeed_vic;
325}
326
327static const TypeInfo aspeed_vic_info = {
328 .name = TYPE_ASPEED_VIC,
329 .parent = TYPE_SYS_BUS_DEVICE,
330 .instance_size = sizeof(AspeedVICState),
331 .class_init = aspeed_vic_class_init,
332};
333
334static void aspeed_vic_register_types(void)
335{
336 type_register_static(&aspeed_vic_info);
337}
338
339type_init(aspeed_vic_register_types);