xref: /openbmc/qemu/hw/intc/aspeed_vic.c (revision 990d2c18)
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 = (const 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     device_class_set_legacy_reset(dc, 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