xref: /openbmc/qemu/hw/intc/aspeed_vic.c (revision 8e6fe6b8)
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);
341