@samitouri / QOSamiQemu / commits / 04f398b2a2

hw/intc: Add l2vic interrupt controller

The Hexagon DSP requires an L2VIC to route up to 1024 external interrupt sources through 4 VID output groups into the core's 8 interrupt inputs. Add a device model for it. Co-authored-by: Matheus Tavares Bernardino <quic_mathbern@quicinc.com> Co-authored-by: Damien Hedde <damien.hedde@dahe.fr> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-8-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Sid Manning committed Aug 5, 2026 at 21:27 UTC 04f398b2a25d0b16339da4d0683ff505e2613ddf
12 files changed +705 -1
MAINTAINERS
+3
@@ -250,6 +250,8 @@ M: Brian Cain <brian.cain@oss.qualcomm.com>
250 R: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
251 S: Supported
252 F: target/hexagon/
253 +F: hw/intc/hex-l2vic.c
254 +F: include/hw/intc/hex-l2vic.h
255 X: target/hexagon/idef-parser/
256 X: target/hexagon/gen_idef_parser_funcs.py
257 F: linux-user/hexagon/
@@ -262,6 +264,7 @@ F: gdbstub/gdb-xml/hexagon*.xml
264 F: docs/system/target-hexagon.rst
265 F: docs/system/hexagon/
266 F: docs/devel/hexagon-sys.rst
267 +F: docs/devel/hexagon-l2vic.rst
268 T: git https://github.com/qualcomm/qemu.git hex-next
269
270 Hexagon idef-parser
docs/devel/hexagon-l2vic.rst new
+55
@@ -0,0 +1,55 @@
1 +.. SPDX-License-Identifier: GPL-2.0-or-later
2 +
3 +Hexagon L2 Vectored Interrupt Controller
4 +========================================
5 +
6 +
7 +.. code-block:: none
8 +
9 + +-------------+ +----------------------+
10 + | l2vic | | hexagon core |
11 + | | | |
12 + IRQ in ---->| | | |
13 + IRQ in ---->| VID0 -|----------------->| irq2 |
14 + ... ---->| | | | |
15 + IRQ in ---->| | | v |
16 + | ... | | <int steering> |
17 + | | | / | | \ |
18 + IRQ in ---->| | | t0 t1 t2 t3 ...|
19 + IRQ in ---->| VIDN -| | |
20 + ... ---->| | | |
21 + IRQ in ---->| | | Global SREG File |
22 + | | | |
23 + | State | | |
24 + | [ ] <--|==================|==> [ VID ] |
25 + | [ ] <--|==================|==> [ VID1 ] |
26 + | | | |
27 + +-------------+ +----------------------+
28 +
29 +L2VIC/Core Integration
30 +----------------------
31 +
32 +* hexagon core supports 8 external interrupt sources
33 +* l2vic supports 1024 input interrupts mapped among 4 output interrupts
34 +* l2vic has four output signals: { VID0, VID1, VID2, VID3 }
35 +* l2vic device has a bank of registers per-VID that can be used to query
36 + the status or assert new interrupts.
37 +* Interrupts are 'steered' to threads based on { thread priority, 'EX' state,
38 + thread interrupt mask, thread interrupt enable, global interrupt enable,
39 + etc. }.
40 +* Any hardware thread could conceivably handle any input interrupt, dependent
41 + on state.
42 +* The system register transfer instruction can read the VID0-VID3 values from
43 + the l2vic when reading from hexagon core system registers "VID" and "VID1".
44 +* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output
45 + IRQ and stores the IRQ number for the VID0 register field. Only after this
46 + interrupt is cleared can the l2vic pulse the VID0 output IRQ again and provide
47 + the next interrupt number on the VID0 register.
48 +* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the
49 + core interrupt. If some/an l2vic VID0 interrupt is pending when this occurs,
50 + the next interrupt should fire and any subsequent reads of the VID register
51 + should reflect the newly raised interrupt.
52 +* In QEMU, on an external interrupt or an unmasked-pending interrupt,
53 + all vCPUs are triggered (has_work==true) and each will grab the IO lock
54 + while considering the steering logic to determine whether they're the thread
55 + that must handle the interrupt.
docs/devel/index-internals.rst
+1
@@ -15,6 +15,7 @@ Details about QEMU's various subsystems including how to add features to them.
15 clocks
16 ebpf_rss
17 hexagon-sys
18 + hexagon-l2vic
19 migration/index
20 multi-process
21 reset
hw/hexagon/Kconfig
+1
@@ -3,6 +3,7 @@ config HEX_DSP
3 default y
4 depends on HEXAGON
5 select CPU_CLUSTER
6 + select HEX_L2VIC
7
8 config HEX_VIRT
9 bool
hw/intc/Kconfig
+3
@@ -8,6 +8,9 @@ config I8259
8 config PL190
9 bool
10
11 +config HEX_L2VIC
12 + bool
13 +
14 config IOAPIC
15 bool
16 select I8259
hw/intc/hex-l2vic.c new
+556
@@ -0,0 +1,556 @@
1 +/*
2 + * QEMU L2VIC Interrupt Controller
3 + *
4 + * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference.
5 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#include "qemu/osdep.h"
10 +#include "hw/core/irq.h"
11 +#include "hw/core/sysbus.h"
12 +#include "migration/vmstate.h"
13 +#include "qemu/log.h"
14 +#include "qemu/module.h"
15 +#include "qemu/bitmap.h"
16 +#include "qemu/bitops.h"
17 +#include "hw/intc/hex-l2vic.h"
18 +#include "trace.h"
19 +
20 +#define L2VIC_VID_GRP_0 0x0 /* Read */
21 +#define L2VIC_VID_GRP_1 0x4 /* Read */
22 +#define L2VIC_VID_GRP_2 0x8 /* Read */
23 +#define L2VIC_VID_GRP_3 0xC /* Read */
24 +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */
25 +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */
26 +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */
27 +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */
28 +#define L2VIC_INT_STATUSn 0x380 /* Read */
29 +#define L2VIC_INT_CLEARn 0x400 /* Write */
30 +#define L2VIC_SOFT_INTn 0x480 /* Write */
31 +#define L2VIC_INT_PENDINGn 0x500 /* Read */
32 +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */
33 +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */
34 +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */
35 +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */
36 +
37 +#define L2VIC_INTERRUPT_MAX 1024
38 +/*
39 + * Note about l2vic groups:
40 + * Each interrupt to L2VIC can be configured to associate with one of
41 + * four groups.
42 + * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default)
43 + * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3)
44 + * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4)
45 + * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5)
46 + */
47 +
48 +static void bitmap32_write_word(uint32_t *bitmap, int word_offset, uint32_t val)
49 +{
50 + bitmap[word_offset] = val;
51 +}
52 +
53 +static void bitmap32_clear_word(uint32_t *bitmap, int word_offset,
54 + uint32_t mask)
55 +{
56 + bitmap[word_offset] &= ~mask;
57 +}
58 +
59 +static void bitmap32_set_word(uint32_t *bitmap, int word_offset, uint32_t mask)
60 +{
61 + bitmap[word_offset] |= mask;
62 +}
63 +
64 +static uint32_t bitmap32_read_word(uint32_t *bitmap, int word_offset)
65 +{
66 + return bitmap[word_offset];
67 +}
68 +
69 +OBJECT_DECLARE_SIMPLE_TYPE(HexL2VICState, HEX_L2VIC)
70 +
71 +#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32)
72 +#define L2VIC_REG_RANGE_SIZE 0x80
73 +
74 +typedef struct HexL2VICState {
75 + SysBusDevice parent_obj;
76 +
77 + MemoryRegion iomem;
78 + MemoryRegion fast_iomem;
79 + /*
80 + * vid_group[i] is readable at L2VIC_VID_GRP_i (offset i*4): the irq
81 + * last delivered through VID group i, 0-1023 so only 10 bits are used.
82 + */
83 + uint32_t vid_group[4];
84 + /*
85 + * Last irq delivered on any VID group; not specific to group 0.
86 + * Used by the ciad path to clear the most-recently-delivered
87 + * interrupt from int_status.
88 + */
89 + uint32_t vid;
90 + DECLARE_BITMAP32(int_enable, L2VIC_INTERRUPT_MAX);
91 + /* Asserted interrupts awaiting delivery once no VID is active */
92 + DECLARE_BITMAP32(int_pending, L2VIC_INTERRUPT_MAX);
93 + /* Which enabled interrupt is active */
94 + DECLARE_BITMAP32(int_status, L2VIC_INTERRUPT_MAX);
95 + /* Edge or Level interrupt */
96 + DECLARE_BITMAP32(int_type, L2VIC_INTERRUPT_MAX);
97 + DECLARE_BITMAP32(int_group_n[4], L2VIC_INTERRUPT_MAX);
98 + qemu_irq irq[8];
99 +} HexL2VICState;
100 +
101 +typedef enum {
102 + L2VIC_OP_WRITE,
103 + L2VIC_OP_CLEAR,
104 + L2VIC_OP_SET,
105 + L2VIC_OP_NONE,
106 +} L2VicWriteOp;
107 +
108 +typedef struct {
109 + hwaddr base;
110 + size_t state_offset;
111 + L2VicWriteOp write_op;
112 + bool write_only;
113 +} L2VicRegRange;
114 +
115 +static const L2VicRegRange l2vic_reg_ranges[] = {
116 + { L2VIC_INT_ENABLEn, offsetof(HexL2VICState, int_enable),
117 + L2VIC_OP_WRITE, false },
118 + { L2VIC_INT_ENABLE_CLEARn, offsetof(HexL2VICState, int_enable),
119 + L2VIC_OP_CLEAR, true },
120 + { L2VIC_INT_ENABLE_SETn, offsetof(HexL2VICState, int_enable),
121 + L2VIC_OP_SET, true },
122 + { L2VIC_INT_TYPEn, offsetof(HexL2VICState, int_type),
123 + L2VIC_OP_WRITE, false },
124 + { L2VIC_INT_STATUSn, offsetof(HexL2VICState, int_status),
125 + L2VIC_OP_NONE, false },
126 + { L2VIC_INT_CLEARn, offsetof(HexL2VICState, int_status),
127 + L2VIC_OP_CLEAR, true },
128 + { L2VIC_SOFT_INTn, offsetof(HexL2VICState, int_pending),
129 + L2VIC_OP_NONE, true },
130 + { L2VIC_INT_PENDINGn, offsetof(HexL2VICState, int_pending),
131 + L2VIC_OP_WRITE, false },
132 + { L2VIC_INT_GRPn_0, offsetof(HexL2VICState, int_group_n[0]),
133 + L2VIC_OP_WRITE, false },
134 + { L2VIC_INT_GRPn_1, offsetof(HexL2VICState, int_group_n[1]),
135 + L2VIC_OP_WRITE, false },
136 + { L2VIC_INT_GRPn_2, offsetof(HexL2VICState, int_group_n[2]),
137 + L2VIC_OP_WRITE, false },
138 + { L2VIC_INT_GRPn_3, offsetof(HexL2VICState, int_group_n[3]),
139 + L2VIC_OP_WRITE, false },
140 +};
141 +
142 +static uint32_t *l2vic_state_bitmap(HexL2VICState *s, size_t state_offset)
143 +{
144 + return (uint32_t *)((char *)s + state_offset);
145 +}
146 +
147 +static bool l2vic_reg_read_range(HexL2VICState *s, hwaddr offset,
148 + uint64_t *value)
149 +{
150 + int i;
151 +
152 + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) {
153 + const L2VicRegRange *r = &l2vic_reg_ranges[i];
154 +
155 + if (offset >= r->base &&
156 + offset < r->base + L2VIC_REG_RANGE_SIZE) {
157 + if (r->write_only) {
158 + *value = 0;
159 + } else {
160 + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset);
161 + *value = bitmap32_read_word(bitmap,
162 + (offset - r->base) >> 2);
163 + }
164 + return true;
165 + }
166 + }
167 + return false;
168 +}
169 +
170 +static bool l2vic_reg_write_range(HexL2VICState *s, hwaddr offset,
171 + uint32_t val)
172 +{
173 + int i;
174 +
175 + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) {
176 + const L2VicRegRange *r = &l2vic_reg_ranges[i];
177 +
178 + if (offset >= r->base &&
179 + offset < r->base + L2VIC_REG_RANGE_SIZE) {
180 + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset);
181 + int word = (offset - r->base) >> 2;
182 +
183 + switch (r->write_op) {
184 + case L2VIC_OP_WRITE:
185 + bitmap32_write_word(bitmap, word, val);
186 + break;
187 + case L2VIC_OP_CLEAR:
188 + bitmap32_clear_word(bitmap, word, val);
189 + break;
190 + case L2VIC_OP_SET:
191 + bitmap32_set_word(bitmap, word, val);
192 + break;
193 + case L2VIC_OP_NONE:
194 + /* Read-only or handled elsewhere; ignore the write. */
195 + break;
196 + default:
197 + g_assert_not_reached();
198 + }
199 + return true;
200 + }
201 + }
202 + return false;
203 +}
204 +
205 +/*
206 + * The four INT_GRPn_* register arrays are interleaved across irqs in
207 + * blocks of 8: irq 0-7 live in group_n[0], irq 8-15 in group_n[1], irq
208 + * 16-23 in group_n[2], irq 24-31 in group_n[3], irq 32-39 back in
209 + * group_n[0], and so on.
210 + */
211 +static uint32_t *get_int_group(HexL2VICState *s, int irq)
212 +{
213 + return s->int_group_n[extract32(irq, 3, 2)];
214 +}
215 +
216 +static int find_slice(int irq)
217 +{
218 + return irq / 32;
219 +}
220 +
221 +static int get_vid(HexL2VICState *s, int irq)
222 +{
223 + uint32_t *group = get_int_group(s, irq);
224 + uint32_t slice = group[find_slice(irq)];
225 + uint32_t vid;
226 + /*
227 + * Each irq occupies a 4-bit field: bit 3 is the group-enable bit,
228 + * bits 0-2 select the VID group. Shift down to this irq's field.
229 + */
230 + uint32_t val = slice >> ((irq & 0x7) * 4);
231 +
232 + if (!(val & 0x8)) {
233 + return 0;
234 + }
235 + vid = val & 0x7;
236 + if (vid >= ARRAY_SIZE(s->vid_group)) {
237 + qemu_log_mask(LOG_GUEST_ERROR,
238 + "L2VIC: irq %d requests invalid vid group %u\n",
239 + irq, vid);
240 + return 0;
241 + }
242 + return vid;
243 +}
244 +
245 +static inline bool vid_active(HexL2VICState *s)
246 +{
247 + const uint32_t size = L2VIC_INTERRUPT_MAX;
248 + const uint32_t active_irq = find_first_bit32(s->int_status, size);
249 + return active_irq != size;
250 +}
251 +
252 +static bool l2vic_update(HexL2VICState *s, int irq)
253 +{
254 + bool pending;
255 + bool enable;
256 +
257 + if (vid_active(s)) {
258 + return true;
259 + }
260 +
261 + pending = test_bit32(irq, s->int_pending);
262 + enable = test_bit32(irq, s->int_enable);
263 + if (pending && enable) {
264 + int vid = get_vid(s, irq);
265 + set_bit32(irq, s->int_status);
266 + clear_bit32(irq, s->int_pending);
267 + /*
268 + * Only auto-disable for edge-triggered interrupts (type=1).
269 + * Level-triggered interrupts (type=0, the default) keep their
270 + * enable bit set across deliveries -- the firmware enables once
271 + * and expects the interrupt to remain enabled.
272 + */
273 + if (test_bit32(irq, s->int_type)) {
274 + clear_bit32(irq, s->int_enable);
275 + }
276 + s->vid = irq;
277 + s->vid_group[vid] = irq;
278 +
279 + qemu_irq_pulse(s->irq[vid + 2]);
280 + trace_hex_l2vic_delivered(irq, vid);
281 + return true;
282 + }
283 + return false;
284 +}
285 +
286 +static void l2vic_update_all(HexL2VICState *s)
287 +{
288 + for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) {
289 + if (l2vic_update(s, i)) {
290 + /* once vid is active, no-one else can set it until ciad */
291 + return;
292 + }
293 + }
294 +}
295 +
296 +static void l2vic_set_irq(void *opaque, int irq, int level)
297 +{
298 + HexL2VICState *s = (HexL2VICState *)opaque;
299 +
300 + if (level) {
301 + set_bit32(irq, s->int_pending);
302 + }
303 + l2vic_update(s, irq);
304 +}
305 +
306 +static void l2vic_write(void *opaque, hwaddr offset, uint64_t val,
307 + unsigned size)
308 +{
309 + HexL2VICState *s = (HexL2VICState *)opaque;
310 +
311 + trace_hex_l2vic_reg_write((unsigned)offset, (uint32_t)val);
312 +
313 + if (!l2vic_reg_write_range(s, offset, val)) {
314 + qemu_log_mask(LOG_UNIMP,
315 + "%s: offset 0x%" HWADDR_PRIx " unimplemented\n",
316 + __func__, offset);
317 + }
318 +
319 + /* SOFT_INT also sets pending for edge-triggered interrupts */
320 + if (offset >= L2VIC_SOFT_INTn &&
321 + offset < L2VIC_SOFT_INTn + L2VIC_REG_RANGE_SIZE && val) {
322 + int base_irq = ((offset - L2VIC_SOFT_INTn) >> 2) * 32;
323 + uint32_t bits = val;
324 + int bit;
325 +
326 + while ((bit = ctz32(bits)) < 32) {
327 + int irq = base_irq + bit;
328 +
329 + if (test_bit32(irq, s->int_type)) {
330 + set_bit32(irq, s->int_pending);
331 + }
332 + bits &= ~(1u << bit);
333 + }
334 + }
335 +
336 + l2vic_update_all(s);
337 +}
338 +
339 +static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size)
340 +{
341 + uint64_t value;
342 + HexL2VICState *s = (HexL2VICState *)opaque;
343 +
344 + if (offset <= L2VIC_VID_GRP_3) {
345 + value = s->vid_group[offset >> 2];
346 + } else if (!l2vic_reg_read_range(s, offset, &value)) {
347 + value = 0;
348 + qemu_log_mask(LOG_GUEST_ERROR,
349 + "L2VIC: %s: offset 0x%" HWADDR_PRIx "\n", __func__,
350 + offset);
351 + }
352 +
353 + trace_hex_l2vic_reg_read((unsigned)offset, (uint32_t)value);
354 + return value;
355 +}
356 +
357 +static const MemoryRegionOps l2vic_ops = {
358 + .read = l2vic_read,
359 + .write = l2vic_write,
360 + .endianness = DEVICE_LITTLE_ENDIAN,
361 + .valid.min_access_size = 4,
362 + .valid.max_access_size = 4,
363 + .valid.unaligned = false,
364 +};
365 +
366 +#define FASTL2VIC_ENABLE 0x0
367 +#define FASTL2VIC_DISABLE 0x1
368 +#define FASTL2VIC_INT 0x2
369 +
370 +static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val,
371 + unsigned size)
372 +{
373 + if (offset == 0) {
374 + uint32_t cmd = (val >> 16) & 0x3;
375 + uint32_t irq = val & 0x3ff;
376 + uint32_t slice = (irq / 32) * 4;
377 + val = 1 << (irq % 32);
378 +
379 + if (cmd == FASTL2VIC_ENABLE) {
380 + l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size);
381 + } else if (cmd == FASTL2VIC_DISABLE) {
382 + l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size);
383 + } else if (cmd == FASTL2VIC_INT) {
384 + l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size);
385 + } else {
386 + qemu_log_mask(LOG_GUEST_ERROR,
387 + "%s: invalid write cmd %" PRId32 "\n",
388 + __func__, cmd);
389 + }
390 + return;
391 + }
392 + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08" HWADDR_PRIx
393 + "\n", __func__, offset);
394 +}
395 +
396 +static uint64_t fastl2vic_read(void *opaque, hwaddr offset, unsigned size)
397 +{
398 + return 0;
399 +}
400 +
401 +static const MemoryRegionOps fastl2vic_ops = {
402 + .read = fastl2vic_read,
403 + .write = fastl2vic_write,
404 + .endianness = DEVICE_LITTLE_ENDIAN,
405 + .valid.min_access_size = 4,
406 + .valid.max_access_size = 4,
407 + .valid.unaligned = false,
408 +};
409 +
410 +static uint32_t l2vic_interface_read_vid_impl(HexL2VicInterface *iface,
411 + uint32_t group)
412 +{
413 + HexL2VICState *s = HEX_L2VIC(iface);
414 + uint32_t result = 0;
415 +
416 + if (group == 0) {
417 + /* VID register combines vid_group[0] (VID0) and vid_group[1] (VID1) */
418 + result = deposit32(result, 0, 16, s->vid_group[0]);
419 + result = deposit32(result, 16, 16, s->vid_group[1]);
420 + } else if (group == 1) {
421 + /* VID1 register combines vid_group[2] (VID2) and vid_group[3] (VID3) */
422 + result = deposit32(result, 0, 16, s->vid_group[2]);
423 + result = deposit32(result, 16, 16, s->vid_group[3]);
424 + }
425 + return result;
426 +}
427 +
428 +static void l2vic_interface_update_vid_impl(HexL2VicInterface *iface,
429 + uint32_t group, uint32_t value)
430 +{
431 + HexL2VICState *s = HEX_L2VIC(iface);
432 +
433 + if (group == 0) {
434 + s->vid_group[0] = extract32(value, 0, 16);
435 + s->vid_group[1] = extract32(value, 16, 16);
436 + } else if (group == 1) {
437 + s->vid_group[2] = extract32(value, 0, 16);
438 + s->vid_group[3] = extract32(value, 16, 16);
439 + }
440 +
441 + l2vic_update_all(s);
442 +}
443 +
444 +static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface)
445 +{
446 + HexL2VICState *s = HEX_L2VIC(iface);
447 +
448 + if (s->vid < L2VIC_INTERRUPT_MAX) {
449 + clear_bit32(s->vid, s->int_status);
450 + }
451 + l2vic_update_all(s);
452 +}
453 +
454 +static void l2vic_reset_hold(Object *obj, ResetType type G_GNUC_UNUSED)
455 +{
456 + HexL2VICState *s = HEX_L2VIC(obj);
457 +
458 + memset(s->int_enable, 0, sizeof(s->int_enable));
459 + memset(s->int_pending, 0, sizeof(s->int_pending));
460 + memset(s->int_status, 0, sizeof(s->int_status));
461 + memset(s->int_type, 0, sizeof(s->int_type));
462 + memset(s->int_group_n, 0, sizeof(s->int_group_n));
463 + memset(s->vid_group, 0, sizeof(s->vid_group));
464 + s->vid = 0;
465 +
466 + l2vic_update_all(s);
467 +}
468 +
469 +static void reset_irq_handler(void *opaque, int irq, int level)
470 +{
471 + Object *obj = OBJECT(opaque);
472 +
473 + if (level) {
474 + l2vic_reset_hold(obj, RESET_TYPE_COLD);
475 + }
476 +}
477 +
478 +static void l2vic_init(Object *obj)
479 +{
480 + DeviceState *dev = DEVICE(obj);
481 + HexL2VICState *s = HEX_L2VIC(obj);
482 + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
483 + int i;
484 +
485 + memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000);
486 + sysbus_init_mmio(sbd, &s->iomem);
487 + memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast",
488 + 0x10000);
489 + sysbus_init_mmio(sbd, &s->fast_iomem);
490 +
491 + qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX);
492 + qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1);
493 + for (i = 0; i < 8; i++) {
494 + sysbus_init_irq(sbd, &s->irq[i]);
495 + }
496 +}
497 +
498 +static const VMStateDescription vmstate_l2vic = {
499 + .name = "l2vic",
500 + .version_id = 1,
501 + .minimum_version_id = 1,
502 + .fields =
503 + (VMStateField[]){
504 + VMSTATE_UINT32_ARRAY(vid_group, HexL2VICState, 4),
505 + VMSTATE_UINT32(vid, HexL2VICState),
506 + VMSTATE_UINT32_ARRAY(int_enable, HexL2VICState, SLICE_MAX),
507 + VMSTATE_UINT32_ARRAY(int_type, HexL2VICState, SLICE_MAX),
508 + VMSTATE_UINT32_ARRAY(int_status, HexL2VICState, SLICE_MAX),
509 + VMSTATE_UINT32_ARRAY(int_pending, HexL2VICState, SLICE_MAX),
510 + VMSTATE_UINT32_2DARRAY(int_group_n, HexL2VICState, 4, SLICE_MAX),
511 + VMSTATE_END_OF_LIST() }
512 +};
513 +
514 +static void l2vic_interface_class_init(ObjectClass *klass, const void *data)
515 +{
516 + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_CLASS(klass);
517 +
518 + k->read_vid = l2vic_interface_read_vid_impl;
519 + k->update_vid = l2vic_interface_update_vid_impl;
520 + k->clear_interrupt = l2vic_interface_clear_interrupt_impl;
521 +}
522 +
523 +static void l2vic_class_init(ObjectClass *klass, const void *data)
524 +{
525 + DeviceClass *dc = DEVICE_CLASS(klass);
526 + ResettableClass *rc = RESETTABLE_CLASS(klass);
527 +
528 + dc->vmsd = &vmstate_l2vic;
529 + rc->phases.hold = l2vic_reset_hold;
530 +}
531 +
532 +static const TypeInfo l2vic_interface_info = {
533 + .name = TYPE_HEX_L2VIC_INTERFACE,
534 + .parent = TYPE_INTERFACE,
535 + .class_size = sizeof(HexL2VicInterfaceClass),
536 + .class_init = l2vic_interface_class_init,
537 +};
538 +
539 +static const TypeInfo l2vic_info = {
540 + .name = TYPE_HEX_L2VIC,
541 + .parent = TYPE_SYS_BUS_DEVICE,
542 + .instance_size = sizeof(HexL2VICState),
543 + .instance_init = l2vic_init,
544 + .class_init = l2vic_class_init,
545 + .interfaces = (InterfaceInfo[]) {
546 + { TYPE_HEX_L2VIC_INTERFACE },
547 + { }
548 + },
549 +};
550 +
551 +static const TypeInfo l2vic_types[] = {
552 + l2vic_interface_info,
553 + l2vic_info,
554 +};
555 +
556 +DEFINE_TYPES(l2vic_types)
hw/intc/meson.build
+2
@@ -74,6 +74,8 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi
74 specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c'))
75 specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
76 if_true: files('spapr_xive_kvm.c'))
77 +
78 +specific_ss.add(when: 'CONFIG_HEX_L2VIC', if_true: files('hex-l2vic.c'))
79 specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
80 specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c'))
81 specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c'))
hw/intc/trace-events
+4
@@ -337,6 +337,10 @@ sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u -
337 sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx"
338 sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx"
339 sh_intc_set(int id, int enable) "setting interrupt group %d to %d"
340 +# hex-l2vic.c
341 +hex_l2vic_reg_write(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32
342 +hex_l2vic_reg_read(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32
343 +hex_l2vic_delivered(int irq, int vid) "l2vic: delivered %d (vid %d)"
344
345 # loongson_ipi.c
346 loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
include/hw/intc/hex-l2vic.h new
+61
@@ -0,0 +1,61 @@
1 +/*
2 + * QEMU L2VIC Interrupt Controller
3 + *
4 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#ifndef HW_INTC_HEX_L2VIC_H
9 +#define HW_INTC_HEX_L2VIC_H
10 +
11 +#include "qom/object.h"
12 +
13 +#define TYPE_HEX_L2VIC "hex-l2vic"
14 +/*
15 + * L2VIC Interface for CPU/GlobalReg interaction
16 + */
17 +#define TYPE_HEX_L2VIC_INTERFACE "hex-l2vic-if"
18 +
19 +typedef struct HexL2VicInterface HexL2VicInterface;
20 +
21 +typedef struct HexL2VicInterfaceClass {
22 + InterfaceClass parent_class;
23 +
24 + uint32_t (*read_vid)(HexL2VicInterface *l2vic, uint32_t group);
25 +
26 + /*
27 + * Write the VID: unpack the fields into per-group VIDs. This does
28 + * not deliver or clear any interrupt; a pending interrupt stays
29 + * gated until ciad.
30 + */
31 + void (*update_vid)(HexL2VicInterface *l2vic, uint32_t group,
32 + uint32_t value);
33 +
34 + /* Clear interrupt using CIAD instruction */
35 + void (*clear_interrupt)(HexL2VicInterface *l2vic);
36 +} HexL2VicInterfaceClass;
37 +
38 +DECLARE_OBJ_CHECKERS(HexL2VicInterface, HexL2VicInterfaceClass,
39 + HEX_L2VIC_INTERFACE, TYPE_HEX_L2VIC_INTERFACE);
40 +
41 +static inline uint32_t l2vic_read_vid(HexL2VicInterface *l2vic,
42 + uint32_t group)
43 +{
44 + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
45 + return k->read_vid(l2vic, group);
46 +}
47 +
48 +static inline void l2vic_update_vid(HexL2VicInterface *l2vic, uint32_t group,
49 + uint32_t value)
50 +{
51 + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
52 + k->update_vid(l2vic, group, value);
53 +}
54 +
55 +static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic)
56 +{
57 + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic);
58 + k->clear_interrupt(l2vic);
59 +}
60 +
61 +#endif /* HW_INTC_HEX_L2VIC_H */
target/hexagon/cpu.c
+2
@@ -65,6 +65,8 @@ static const Property hexagon_cpu_properties[] = {
65 DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
66 HexagonTLBState *),
67 DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff),
68 + DEFINE_PROP_LINK("l2vic", HexagonCPU, l2vic,
69 + TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *),
70 DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
71 TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
72 DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0),
target/hexagon/cpu.h
+2
@@ -39,6 +39,7 @@ typedef struct HexagonGlobalRegState HexagonGlobalRegState;
39 #include "qemu/bitmap.h"
40
41 #include "target/hexagon/reg_fields.h"
42 +#include "hw/intc/hex-l2vic.h"
43
44 #define NUM_PREGS 4
45 #define TOTAL_PER_THREAD_REGS 64
@@ -198,6 +199,7 @@ struct ArchCPU {
199 uint32_t boot_addr;
200 HexagonGlobalRegState *globalregs;
201 uint32_t htid;
202 + HexL2VicInterface *l2vic;
203 #endif
204 };
205
target/hexagon/op_helper.c
+15 -1
@@ -40,6 +40,7 @@
40 #include "hw/hexagon/hexagon_globalreg.h"
41 #include "hex_mmu.h"
42 #include "hw/hexagon/hexagon_tlb.h"
43 +#include "hw/intc/hex-l2vic.h"
44 #include "hex_interrupts.h"
45 #include "hexswi.h"
46 #endif
@@ -1564,7 +1565,20 @@ void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot,
1565
1566 void HELPER(ciad)(CPUHexagonState *env, uint32_t mask)
1567 {
1567 - g_assert_not_reached();
1568 + uint32_t ipendad;
1569 + uint32_t iad;
1570 + HexagonCPU *cpu;
1571 +
1572 + BQL_LOCK_GUARD();
1573 + cpu = env_archcpu(env);
1574 + ipendad = hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD,
1575 + env->threadId);
1576 + iad = fGET_FIELD(ipendad, IPENDAD_IAD);
1577 + fSET_FIELD(ipendad, IPENDAD_IAD, iad & ~(mask));
1578 + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD,
1579 + ipendad, env->threadId);
1580 + l2vic_clear_interrupt(cpu->l2vic);
1581 + hex_interrupt_update(env);
1582 }
1583
1584 void HELPER(siad)(CPUHexagonState *env, uint32_t mask)