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)