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