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