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