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