hw/timer: Add QCT QTimer device model
Implement the QCT QTimer generic timer device used by Hexagon DSP systems. Co-authored-by: Damien Hedde <damien.hedde@greensocs.com> Co-authored-by: Tobias Röhmel <quic_trohmel@quicinc.com> Co-authored-by: Sid Manning <sidneym@quicinc.com> Co-authored-by: Thomas Marceron <tmarcero@qti.qualcomm.com> Co-authored-by: Mahmoud Kamel <mkamel@qti.qualcomm.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-16-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Brian Cain committed
Aug 5, 2026 at 21:27 UTC
954f0cec387e78fcbb6847f42bdd0c6794a5ed6e
6 files changed
+722
MAINTAINERS
+2
@@ -253,6 +253,8 @@ F: target/hexagon/
253
F: hw/intc/hex-l2vic.c
254
F: include/hw/intc/hex-l2vic.h
255
F: tests/qtest/l2vic-test.c
256
+F: hw/timer/qct-qtimer.c
257
+F: include/hw/timer/qct-qtimer.h
258
X: target/hexagon/idef-parser/
259
X: target/hexagon/gen_idef_parser_funcs.py
260
F: linux-user/hexagon/
hw/timer/Kconfig
+3
@@ -65,3 +65,6 @@ config STELLARIS_GPTM
65
66
config AVR_TIMER16
67
bool
68
+
69
+config HEX_QTIMER
70
+ bool
hw/timer/meson.build
+2
@@ -34,3 +34,5 @@ specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
34
system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c'))
35
36
system_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c'))
37
+
38
+system_ss.add(when: 'CONFIG_HEX_QTIMER', if_true: files('qct-qtimer.c'))
hw/timer/qct-qtimer.c
new
+667
@@ -0,0 +1,667 @@
1
+/*
2
+ * Qualcomm QCT QTimer
3
+ *
4
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5
+ * SPDX-License-Identifier: GPL-2.0-or-later
6
+ */
7
+
8
+#include "qemu/osdep.h"
9
+#include "hw/core/irq.h"
10
+#include "hw/core/qdev-properties.h"
11
+#include "hw/core/sysbus.h"
12
+#include "hw/timer/qct-qtimer.h"
13
+#include "migration/vmstate.h"
14
+#include "qemu/bitops.h"
15
+#include "qemu/log.h"
16
+#include "qemu/module.h"
17
+#include "qemu/timer.h"
18
+#include "qapi/error.h"
19
+#include "trace.h"
20
+
21
+#define QTIMER_MEM_SIZE_BYTES 0x1000
22
+#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL
23
+
24
+#define QCT_QTIMER_TIMER_FRAME_ELTS (16)
25
+#define QCT_QTIMER_TIMER_VIEW_ELTS (2)
26
+
27
+#define QCT_QTIMER_AC_CNTFRQ (0x000)
28
+#define QCT_QTIMER_AC_CNTSR (0x004)
29
+#define QCT_QTIMER_AC_CNTTID_0 (0x08)
30
+#define QCT_QTIMER_AC_CNTACR_START (0x40)
31
+#define QCT_QTIMER_AC_CNTACR_END (0x5c)
32
+#define QCT_QTIMER_AC_CNTTID_1 (0x108)
33
+#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */
34
+#define QCT_QTIMER_AC_CNTACR_RWVT (1 << 4) /* R/W of CNTV_* regs */
35
+#define QCT_QTIMER_AC_CNTACR_RVOFF (1 << 3) /* R/W of CNTVOFF register */
36
+#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */
37
+#define QCT_QTIMER_AC_CNTACR_RPVCT (1 << 1) /* R/W of CNTVCT register */
38
+#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */
39
+#define QCT_QTIMER_VERSION (0x0fd0)
40
+
41
+#define QCT_QTIMER_CNTPCT_LO (0x000)
42
+#define QCT_QTIMER_CNTPCT_HI (0x004)
43
+#define QCT_QTIMER_CNT_FREQ (0x010)
44
+#define QCT_QTIMER_CNTPL0ACR (0x014)
45
+#define QCT_QTIMER_CNTPL0ACR_PL0CTEN (1 << 9)
46
+#define QCT_QTIMER_CNTPL0ACR_PL0TVEN (1 << 8)
47
+#define QCT_QTIMER_CNTPL0ACR_PL0VCTEN (1 << 1)
48
+#define QCT_QTIMER_CNTPL0ACR_PL0PCTEN (1 << 0)
49
+#define QCT_QTIMER_CNTP_CVAL_LO (0x020)
50
+#define QCT_QTIMER_CNTP_CVAL_HI (0x024)
51
+#define QCT_QTIMER_CNT_MASK 0x00ffffffffffffffULL
52
+#define QCT_QTIMER_CNT_HI_BITS 24
53
+#define QCT_QTIMER_CNTP_TVAL (0x028)
54
+#define QCT_QTIMER_CNTP_CTL (0x02c)
55
+#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0)
56
+#define QCT_QTIMER_CNTP_CTL_INTEN (1 << 1)
57
+#define QCT_QTIMER_CNTP_CTL_ISTAT (1 << 2)
58
+
59
+OBJECT_DECLARE_SIMPLE_TYPE(QCTQtimerState, QCT_QTIMER)
60
+
61
+typedef struct QCTHextimerState {
62
+ QCTQtimerState *qtimer;
63
+ QEMUTimer *timer; /* one-shot deadline timer */
64
+ int64_t offset_ns; /* QEMU_CLOCK_VIRTUAL ns at which cntpct == 0 */
65
+ uint64_t cntval; /* 64-bit physical timer compare value */
66
+ uint32_t control;
67
+ uint32_t cnt_ctrl;
68
+ uint32_t cntpl0acr;
69
+ uint32_t int_level;
70
+ qemu_irq irq;
71
+} QCTHextimerState;
72
+
73
+struct QCTQtimerState {
74
+ SysBusDevice parent_obj;
75
+
76
+ MemoryRegion iomem;
77
+ MemoryRegion view_iomem;
78
+ uint32_t secure;
79
+ QCTHextimerState timer[QCT_QTIMER_TIMER_FRAME_ELTS];
80
+ uint32_t freq_hz;
81
+ uint32_t nr_frames;
82
+ uint32_t nr_views;
83
+ uint32_t frame_stride;
84
+ uint32_t freq_scale;
85
+};
86
+
87
+/*
88
+ * QTimer version register:
89
+ *
90
+ * 3 2 1
91
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
92
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93
+ * | Major | Minor | Step |
94
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95
+ */
96
+#define QCT_QTIMER_VERSION_VALUE 0x20020000
97
+
98
+static uint32_t qct_qtimer_cnttid(QCTQtimerState *s, unsigned int half)
99
+{
100
+ uint32_t nibble = 0x1 | (s->nr_views > 1 ? 0x4 : 0x0);
101
+ uint32_t base = half * 8;
102
+ uint32_t cnttid = 0;
103
+ unsigned int i;
104
+
105
+ for (i = 0; i < 8; i++) {
106
+ if (base + i < s->nr_frames) {
107
+ cnttid |= nibble << (i * 4);
108
+ }
109
+ }
110
+ return cnttid;
111
+}
112
+
113
+/* Counter value derived on-demand from QEMU_CLOCK_VIRTUAL. */
114
+static uint64_t hex_timer_now(QCTHextimerState *s)
115
+{
116
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
117
+ uint32_t scale;
118
+ uint64_t scaled_elapsed;
119
+
120
+ if (now <= s->offset_ns) {
121
+ return 0;
122
+ }
123
+ scale = MAX(s->qtimer->freq_scale, 1u);
124
+ scaled_elapsed = (uint64_t)(now - s->offset_ns) / scale;
125
+ return muldiv64(scaled_elapsed, s->qtimer->freq_hz,
126
+ NANOSECONDS_PER_SECOND) &
127
+ QCT_QTIMER_CNT_MASK;
128
+}
129
+
130
+/* Arm (or disarm) the one-shot deadline timer. */
131
+static void hex_timer_rearm(QCTHextimerState *s)
132
+{
133
+ uint32_t scale;
134
+ uint64_t base_ns;
135
+ int64_t deadline_ns;
136
+
137
+ if (!(s->control & QCT_QTIMER_CNTP_CTL_ENABLE)) {
138
+ timer_del(s->timer);
139
+ return;
140
+ }
141
+
142
+ scale = MAX(s->qtimer->freq_scale, 1u);
143
+ /*
144
+ * Round the ticks-to-ns conversion up so that hex_timer_now(), which
145
+ * truncates when it divides elapsed ns by scale, is guaranteed to
146
+ * report >= cntval once this deadline fires. A truncating conversion
147
+ * here could re-arm at the same deadline forever when scale > 1.
148
+ */
149
+ base_ns = muldiv64_round_up(s->cntval, NANOSECONDS_PER_SECOND,
150
+ s->qtimer->freq_hz);
151
+ if (base_ns >
152
+ ((uint64_t)INT64_MAX - (uint64_t)s->offset_ns) / scale) {
153
+ timer_del(s->timer);
154
+ return;
155
+ }
156
+ deadline_ns = s->offset_ns + (int64_t)(base_ns * scale);
157
+ timer_mod(s->timer, deadline_ns);
158
+}
159
+
160
+static void hex_timer_update(QCTHextimerState *s)
161
+{
162
+ int level = s->int_level &&
163
+ (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) &&
164
+ !(s->control & QCT_QTIMER_CNTP_CTL_INTEN);
165
+
166
+ trace_qtimer_interrupt();
167
+ qemu_set_irq(s->irq, level);
168
+}
169
+
170
+/*
171
+ * Access-control (AC) region: offsets below 0x1000, gates CNTFRQ/CNTSR/
172
+ * CNTTID/CNTACR per frame plus the shared VERSION register.
173
+ */
174
+static uint64_t qct_qtimer_ac_read(void *opaque, hwaddr offset, unsigned size)
175
+{
176
+ QCTQtimerState *s = opaque;
177
+ uint32_t frame;
178
+
179
+ switch (offset) {
180
+ case QCT_QTIMER_AC_CNTFRQ:
181
+ return s->freq_hz;
182
+ case QCT_QTIMER_AC_CNTSR:
183
+ return s->secure;
184
+ case QCT_QTIMER_AC_CNTTID_0:
185
+ return qct_qtimer_cnttid(s, 0);
186
+ case QCT_QTIMER_AC_CNTTID_1:
187
+ return qct_qtimer_cnttid(s, 1);
188
+ case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END:
189
+ frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4;
190
+ if (frame >= s->nr_frames) {
191
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n",
192
+ __func__, (int)offset);
193
+ return 0;
194
+ }
195
+ return s->timer[frame].cnt_ctrl;
196
+ case QCT_QTIMER_VERSION:
197
+ return QCT_QTIMER_VERSION_VALUE;
198
+ default:
199
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__,
200
+ (int)offset);
201
+ return 0;
202
+ }
203
+}
204
+
205
+static void qct_qtimer_ac_write(void *opaque, hwaddr offset, uint64_t value,
206
+ unsigned size)
207
+{
208
+ QCTQtimerState *s = opaque;
209
+ uint32_t frame;
210
+
211
+ switch (offset) {
212
+ case QCT_QTIMER_AC_CNTFRQ:
213
+ if (value == 0) {
214
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTFRQ value 0\n",
215
+ __func__);
216
+ return;
217
+ }
218
+ s->freq_hz = value;
219
+ return;
220
+ case QCT_QTIMER_AC_CNTSR:
221
+ if (value > 0xff) {
222
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTSR value 0x%x\n",
223
+ __func__, (int)value);
224
+ return;
225
+ }
226
+ s->secure = value;
227
+ return;
228
+ case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END:
229
+ frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4;
230
+ if (frame >= s->nr_frames) {
231
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n",
232
+ __func__, (int)offset);
233
+ return;
234
+ }
235
+ s->timer[frame].cnt_ctrl = value;
236
+ return;
237
+ default:
238
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__,
239
+ (int)offset);
240
+ return;
241
+ }
242
+}
243
+
244
+static const MemoryRegionOps qct_qtimer_ac_ops = {
245
+ .read = qct_qtimer_ac_read,
246
+ .write = qct_qtimer_ac_write,
247
+ .endianness = DEVICE_LITTLE_ENDIAN,
248
+ .valid = {
249
+ .min_access_size = 4,
250
+ .max_access_size = 4,
251
+ .unaligned = false,
252
+ },
253
+ .impl = {
254
+ .min_access_size = 4,
255
+ .max_access_size = 4,
256
+ },
257
+};
258
+
259
+/*
260
+ * View region: a flat array of (frame, view) slots, each frame_stride
261
+ * bytes wide, holding the per-frame CNTPCT/CNTP_CVAL/CNTP_TVAL/CNTP_CTL
262
+ * register set.
263
+ */
264
+static QCTHextimerState *qct_qtimer_demux(QCTQtimerState *s, hwaddr offset,
265
+ uint32_t *reg_offset,
266
+ uint32_t *view)
267
+{
268
+ uint32_t stride = s->frame_stride;
269
+ uint32_t stride_shift = ctz32(stride);
270
+ uint32_t slot_nr = offset >> stride_shift;
271
+ uint32_t frame = slot_nr / s->nr_views;
272
+
273
+ *reg_offset = offset & (stride - 1);
274
+ *view = slot_nr % s->nr_views;
275
+ if (frame >= s->nr_frames) {
276
+ return NULL;
277
+ }
278
+ return &s->timer[frame];
279
+}
280
+
281
+/* Frames 8+ are described by CNTTID_1; each frame's 2nd view is a gated bit. */
282
+static bool qct_qtimer_view_visible(QCTQtimerState *s, uint32_t frame,
283
+ uint32_t view)
284
+{
285
+ uint32_t cnttid = qct_qtimer_cnttid(s, frame < 8 ? 0 : 1);
286
+ uint32_t frame_idx = frame < 8 ? frame : frame - 8;
287
+
288
+ return !view || (cnttid & (0x4 << (frame_idx * 4)));
289
+}
290
+
291
+static bool access_ok(QCTHextimerState *s, uint32_t reg_offset, uint32_t view)
292
+{
293
+ uint32_t acr;
294
+ uint32_t pl0acr;
295
+
296
+ switch (reg_offset) {
297
+ case QCT_QTIMER_CNT_FREQ:
298
+ acr = QCT_QTIMER_AC_CNTACR_RFRQ;
299
+ pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN |
300
+ QCT_QTIMER_CNTPL0ACR_PL0VCTEN;
301
+ break;
302
+ case QCT_QTIMER_CNTPCT_LO:
303
+ case QCT_QTIMER_CNTPCT_HI:
304
+ acr = QCT_QTIMER_AC_CNTACR_RPCT;
305
+ pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN;
306
+ break;
307
+ case QCT_QTIMER_CNTP_CVAL_LO:
308
+ case QCT_QTIMER_CNTP_CVAL_HI:
309
+ case QCT_QTIMER_CNTP_TVAL:
310
+ case QCT_QTIMER_CNTP_CTL:
311
+ acr = QCT_QTIMER_AC_CNTACR_RWPT;
312
+ pl0acr = QCT_QTIMER_CNTPL0ACR_PL0CTEN;
313
+ break;
314
+ default:
315
+ /* CNTPL0ACR and VERSION are ungated. */
316
+ return true;
317
+ }
318
+
319
+ if (!(s->cnt_ctrl & acr)) {
320
+ return false;
321
+ }
322
+ return !view || (s->cntpl0acr & pl0acr);
323
+}
324
+
325
+static MemTxResult hex_timer_read(void *opaque, hwaddr offset, uint64_t *data,
326
+ unsigned size, MemTxAttrs attrs)
327
+{
328
+ QCTQtimerState *qs = opaque;
329
+ uint32_t reg_offset;
330
+ uint32_t view;
331
+ QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view);
332
+ uint32_t frame;
333
+
334
+ if (!s) {
335
+ *data = 0;
336
+ return MEMTX_ACCESS_ERROR;
337
+ }
338
+ frame = s - qs->timer;
339
+
340
+ trace_qtimer_read(offset);
341
+
342
+ if (!qct_qtimer_view_visible(qs, frame, view)) {
343
+ *data = 0;
344
+ return MEMTX_OK;
345
+ }
346
+
347
+ if (!access_ok(s, reg_offset, view)) {
348
+ return MEMTX_ACCESS_ERROR;
349
+ }
350
+
351
+ switch (reg_offset) {
352
+ case QCT_QTIMER_CNT_FREQ:
353
+ *data = s->qtimer->freq_hz;
354
+ return MEMTX_OK;
355
+ case QCT_QTIMER_CNTP_CVAL_LO:
356
+ *data = extract64(s->cntval, 0, 32);
357
+ return MEMTX_OK;
358
+ case QCT_QTIMER_CNTP_CVAL_HI:
359
+ /* HI half is 24-bit per TRM; bits [31:24] are reserved. */
360
+ *data = extract64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS);
361
+ return MEMTX_OK;
362
+ case QCT_QTIMER_CNTPCT_LO:
363
+ *data = extract64(hex_timer_now(s), 0, 32);
364
+ return MEMTX_OK;
365
+ case QCT_QTIMER_CNTPCT_HI:
366
+ *data = extract64(hex_timer_now(s), 32, QCT_QTIMER_CNT_HI_BITS);
367
+ return MEMTX_OK;
368
+ case QCT_QTIMER_CNTP_TVAL:
369
+ *data = (uint32_t)(int32_t)(int64_t)(s->cntval - hex_timer_now(s));
370
+ return MEMTX_OK;
371
+ case QCT_QTIMER_CNTP_CTL:
372
+ /*
373
+ * CNTP_CTL: bit 0 EN, bit 1 IMASK, bit 2 ISTAT (interrupt
374
+ * pending). ISTAT tracks int_level and is read-only.
375
+ */
376
+ *data = s->control | ((s->int_level & 0x1) << 2);
377
+ return MEMTX_OK;
378
+ case QCT_QTIMER_CNTPL0ACR:
379
+ *data = view ? 0 : s->cntpl0acr;
380
+ return MEMTX_OK;
381
+ case QCT_QTIMER_VERSION:
382
+ *data = QCT_QTIMER_VERSION_VALUE;
383
+ return MEMTX_OK;
384
+ default:
385
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__,
386
+ (int)offset);
387
+ *data = 0;
388
+ return MEMTX_ACCESS_ERROR;
389
+ }
390
+}
391
+
392
+static MemTxResult hex_timer_write(void *opaque, hwaddr offset,
393
+ uint64_t value, unsigned size,
394
+ MemTxAttrs attrs)
395
+{
396
+ QCTQtimerState *qs = opaque;
397
+ uint32_t reg_offset;
398
+ uint32_t view;
399
+ QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view);
400
+ uint32_t frame;
401
+
402
+ if (!s) {
403
+ return MEMTX_ACCESS_ERROR;
404
+ }
405
+ frame = s - qs->timer;
406
+
407
+ trace_qtimer_write(offset, value);
408
+
409
+ if (!qct_qtimer_view_visible(qs, frame, view)) {
410
+ return MEMTX_OK;
411
+ }
412
+
413
+ if (!access_ok(s, reg_offset, view)) {
414
+ return MEMTX_ACCESS_ERROR;
415
+ }
416
+
417
+ switch (reg_offset) {
418
+ case QCT_QTIMER_CNTP_CVAL_LO:
419
+ s->int_level = 0;
420
+ s->cntval = deposit64(s->cntval, 0, 32, value);
421
+ hex_timer_rearm(s);
422
+ break;
423
+ case QCT_QTIMER_CNTP_CVAL_HI:
424
+ s->int_level = 0;
425
+ /* HI half is 24-bit per TRM; bits [31:24] are reserved. */
426
+ s->cntval = deposit64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS, value) &
427
+ QCT_QTIMER_CNT_MASK;
428
+ hex_timer_rearm(s);
429
+ break;
430
+ case QCT_QTIMER_CNTP_CTL:
431
+ /* ISTAT (bit 2) is read-only; keep SW writes from polluting it. */
432
+ s->control = value & ~QCT_QTIMER_CNTP_CTL_ISTAT;
433
+ hex_timer_rearm(s);
434
+ break;
435
+ case QCT_QTIMER_CNTP_TVAL:
436
+ /* TVAL write: CVAL = CNTPCT + TVAL (TVAL is signed 32-bit). */
437
+ s->int_level = 0;
438
+ s->cntval = (hex_timer_now(s) + (int64_t)(int32_t)value) &
439
+ QCT_QTIMER_CNT_MASK;
440
+ hex_timer_rearm(s);
441
+ break;
442
+ case QCT_QTIMER_CNTPL0ACR:
443
+ if (!view) {
444
+ s->cntpl0acr = value;
445
+ }
446
+ break;
447
+ default:
448
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__,
449
+ (int)offset);
450
+ return MEMTX_ACCESS_ERROR;
451
+ }
452
+ hex_timer_update(s);
453
+ return MEMTX_OK;
454
+}
455
+
456
+static void hex_timer_tick(void *opaque)
457
+{
458
+ QCTHextimerState *s = opaque;
459
+ uint64_t now = hex_timer_now(s);
460
+ uint64_t diff56 = (now - s->cntval) & QCT_QTIMER_CNT_MASK;
461
+ int64_t signed_diff = (int64_t)(diff56 << 8) >> 8;
462
+
463
+ if (signed_diff >= 0) {
464
+ s->int_level = 1;
465
+ hex_timer_update(s);
466
+ } else {
467
+ hex_timer_rearm(s);
468
+ }
469
+}
470
+
471
+static const MemoryRegionOps hex_timer_ops = {
472
+ .read_with_attrs = hex_timer_read,
473
+ .write_with_attrs = hex_timer_write,
474
+ .endianness = DEVICE_LITTLE_ENDIAN,
475
+ .valid = {
476
+ .min_access_size = 4,
477
+ .max_access_size = 8,
478
+ .unaligned = false,
479
+ },
480
+ .impl = {
481
+ .min_access_size = 4,
482
+ .max_access_size = 4,
483
+ },
484
+};
485
+
486
+static const VMStateDescription vmstate_qct_hextimer = {
487
+ .name = "qct-hextimer",
488
+ .version_id = 1,
489
+ .minimum_version_id = 1,
490
+ .fields = (const VMStateField[]) {
491
+ VMSTATE_UINT32(control, QCTHextimerState),
492
+ VMSTATE_UINT32(cnt_ctrl, QCTHextimerState),
493
+ VMSTATE_INT64(offset_ns, QCTHextimerState),
494
+ VMSTATE_UINT64(cntval, QCTHextimerState),
495
+ VMSTATE_UINT32(cntpl0acr, QCTHextimerState),
496
+ VMSTATE_UINT32(int_level, QCTHextimerState),
497
+ VMSTATE_TIMER_PTR(timer, QCTHextimerState),
498
+ VMSTATE_END_OF_LIST()
499
+ }
500
+};
501
+
502
+static const VMStateDescription vmstate_qct_qtimer = {
503
+ .name = "qct-qtimer",
504
+ .version_id = 1,
505
+ .minimum_version_id = 1,
506
+ .fields = (const VMStateField[]) {
507
+ VMSTATE_UINT32(freq_hz, QCTQtimerState),
508
+ VMSTATE_UINT32(secure, QCTQtimerState),
509
+ VMSTATE_STRUCT_VARRAY_UINT32(timer, QCTQtimerState, nr_frames,
510
+ 1, vmstate_qct_hextimer, QCTHextimerState),
511
+ VMSTATE_END_OF_LIST()
512
+ }
513
+};
514
+
515
+static void qct_qtimer_realize(DeviceState *dev, Error **errp)
516
+{
517
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
518
+ QCTQtimerState *s = QCT_QTIMER(dev);
519
+ unsigned int i;
520
+
521
+ if (s->nr_frames > QCT_QTIMER_TIMER_FRAME_ELTS) {
522
+ error_setg(errp, "nr_frames too high");
523
+ return;
524
+ }
525
+ if (s->nr_views > QCT_QTIMER_TIMER_VIEW_ELTS) {
526
+ error_setg(errp, "nr_views too high");
527
+ return;
528
+ }
529
+ if (s->freq_hz == 0) {
530
+ error_setg(errp, "freq-hz must be nonzero");
531
+ return;
532
+ }
533
+ if (s->frame_stride == 0 || !is_power_of_2(s->frame_stride)) {
534
+ error_setg(errp, "frame_stride must be a nonzero power of two");
535
+ return;
536
+ }
537
+
538
+ memory_region_init_io(&s->iomem, OBJECT(s), &qct_qtimer_ac_ops, s,
539
+ "qct-qtimer-ac", QTIMER_MEM_SIZE_BYTES);
540
+ sysbus_init_mmio(sbd, &s->iomem);
541
+
542
+ memory_region_init_io(&s->view_iomem, OBJECT(s), &hex_timer_ops, s,
543
+ "qct-qtimer-view",
544
+ (uint64_t)s->frame_stride * s->nr_frames *
545
+ s->nr_views);
546
+ sysbus_init_mmio(sbd, &s->view_iomem);
547
+
548
+ for (i = 0; i < s->nr_frames; i++) {
549
+ QCTHextimerState *t = &s->timer[i];
550
+
551
+ t->qtimer = s;
552
+ s->secure |= (1 << i);
553
+
554
+ sysbus_init_irq(sbd, &t->irq);
555
+ t->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hex_timer_tick, t);
556
+ }
557
+}
558
+
559
+static void qct_qtimer_unrealize(DeviceState *dev)
560
+{
561
+ QCTQtimerState *s = QCT_QTIMER(dev);
562
+ unsigned int i;
563
+
564
+ for (i = 0; i < s->nr_frames; i++) {
565
+ QCTHextimerState *t = &s->timer[i];
566
+
567
+ if (t->timer) {
568
+ timer_free(t->timer);
569
+ t->timer = NULL;
570
+ }
571
+ }
572
+}
573
+
574
+static void qct_qtimer_reset_hold(Object *obj, ResetType type)
575
+{
576
+ QCTQtimerState *s = QCT_QTIMER(obj);
577
+ unsigned int i;
578
+
579
+ for (i = 0; i < s->nr_frames; i++) {
580
+ QCTHextimerState *t = &s->timer[i];
581
+
582
+ /*
583
+ * Per TRM: CTL = 0 (EN=0, IMASK=0, ISTAT=0), CVAL = 0 so that
584
+ * TVAL (= CVAL - CNTPCT) also reads 0 at reset. The QEMUTimer is
585
+ * only armed when SW sets CTL.EN=1, so cntval=0 does not cause a
586
+ * spurious fire before SW programs the compare value.
587
+ */
588
+ t->control = 0;
589
+ t->cnt_ctrl = QCT_QTIMER_AC_CNTACR_RWPT | QCT_QTIMER_AC_CNTACR_RWVT |
590
+ QCT_QTIMER_AC_CNTACR_RVOFF | QCT_QTIMER_AC_CNTACR_RFRQ |
591
+ QCT_QTIMER_AC_CNTACR_RPVCT | QCT_QTIMER_AC_CNTACR_RPCT;
592
+ t->cntval = 0;
593
+ t->cntpl0acr = 0;
594
+ t->int_level = 0;
595
+ t->offset_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
596
+ timer_del(t->timer);
597
+ qemu_set_irq(t->irq, 0);
598
+ }
599
+}
600
+
601
+static const Property qct_qtimer_properties[] = {
602
+ DEFINE_PROP_UINT32("freq-hz", QCTQtimerState, freq_hz,
603
+ QTIMER_DEFAULT_FREQ_HZ),
604
+ DEFINE_PROP_UINT32("freq-scale", QCTQtimerState, freq_scale, 1),
605
+ DEFINE_PROP_UINT32("nr_frames", QCTQtimerState, nr_frames, 2),
606
+ DEFINE_PROP_UINT32("nr_views", QCTQtimerState, nr_views, 1),
607
+ DEFINE_PROP_UINT32("frame_stride", QCTQtimerState, frame_stride, 0x1000),
608
+};
609
+
610
+static void qct_qtimer_class_init(ObjectClass *klass, const void *data)
611
+{
612
+ DeviceClass *dc = DEVICE_CLASS(klass);
613
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
614
+
615
+ device_class_set_props(dc, qct_qtimer_properties);
616
+ dc->realize = qct_qtimer_realize;
617
+ dc->unrealize = qct_qtimer_unrealize;
618
+ dc->vmsd = &vmstate_qct_qtimer;
619
+ rc->phases.hold = qct_qtimer_reset_hold;
620
+}
621
+
622
+/* QTimer interface implementation, backing HEX_SREG_TIMERLO/TIMERHI */
623
+static uint32_t qct_qtimer_get_timer_lo_impl(const QctQtimerInterface *obj)
624
+{
625
+ QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj);
626
+
627
+ return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 0, 32)
628
+ : 0;
629
+}
630
+
631
+static uint32_t qct_qtimer_get_timer_hi_impl(const QctQtimerInterface *obj)
632
+{
633
+ QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj);
634
+
635
+ return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 32, 32)
636
+ : 0;
637
+}
638
+
639
+static void qct_qtimer_interface_class_init(ObjectClass *klass,
640
+ const void *data)
641
+{
642
+ QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_CLASS(klass);
643
+
644
+ k->get_timer_lo = qct_qtimer_get_timer_lo_impl;
645
+ k->get_timer_hi = qct_qtimer_get_timer_hi_impl;
646
+}
647
+
648
+static const TypeInfo qct_qtimer_types[] = {
649
+ {
650
+ .name = TYPE_QCT_QTIMER_INTERFACE,
651
+ .parent = TYPE_INTERFACE,
652
+ .class_size = sizeof(QctQtimerInterfaceClass),
653
+ .class_init = qct_qtimer_interface_class_init,
654
+ },
655
+ {
656
+ .name = TYPE_QCT_QTIMER,
657
+ .parent = TYPE_SYS_BUS_DEVICE,
658
+ .instance_size = sizeof(QCTQtimerState),
659
+ .class_init = qct_qtimer_class_init,
660
+ .interfaces = (InterfaceInfo[]) {
661
+ { TYPE_QCT_QTIMER_INTERFACE },
662
+ { }
663
+ },
664
+ },
665
+};
666
+
667
+DEFINE_TYPES(qct_qtimer_types)
hw/timer/trace-events
+5
@@ -128,3 +128,8 @@ imx_epit_get_freq(uint32_t freq) "ptimer frequency is %u"
128
imx_epit_read(const char *name, uint32_t value) "(%s) = 0x%08x"
129
imx_epit_write(const char *name, uint64_t value) "(%s, value = 0x%08" PRIx64 ")"
130
imx_epit_cmp(uint32_t sr) "sr was %d"
131
+
132
+# qct-qtimer.c
133
+qtimer_interrupt(void) "qtimer interrupt line updated"
134
+qtimer_read(uint64_t offset) "offset 0x%" PRIx64
135
+qtimer_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64
include/hw/timer/qct-qtimer.h
new
+43
@@ -0,0 +1,43 @@
1
+/*
2
+ * Qualcomm QCT QTimer
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_TIMER_QCT_QTIMER_H
9
+#define HW_TIMER_QCT_QTIMER_H
10
+
11
+#include "qom/object.h"
12
+
13
+#define TYPE_QCT_QTIMER "qct-qtimer"
14
+
15
+/* QTimer interface for external access from hexagon_globalreg */
16
+#define TYPE_QCT_QTIMER_INTERFACE "qct-qtimer-if"
17
+
18
+typedef struct QctQtimerInterface QctQtimerInterface;
19
+
20
+typedef struct QctQtimerInterfaceClass {
21
+ InterfaceClass parent_class;
22
+
23
+ /* Read the live physical counter, backing HEX_SREG_TIMERLO/TIMERHI */
24
+ uint32_t (*get_timer_lo)(const QctQtimerInterface *qtimer);
25
+ uint32_t (*get_timer_hi)(const QctQtimerInterface *qtimer);
26
+} QctQtimerInterfaceClass;
27
+
28
+DECLARE_OBJ_CHECKERS(QctQtimerInterface, QctQtimerInterfaceClass,
29
+ QCT_QTIMER_INTERFACE, TYPE_QCT_QTIMER_INTERFACE);
30
+
31
+static inline uint32_t qct_qtimer_get_timer_lo(const QctQtimerInterface *qtimer)
32
+{
33
+ QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer);
34
+ return k->get_timer_lo(qtimer);
35
+}
36
+
37
+static inline uint32_t qct_qtimer_get_timer_hi(const QctQtimerInterface *qtimer)
38
+{
39
+ QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer);
40
+ return k->get_timer_hi(qtimer);
41
+}
42
+
43
+#endif /* HW_TIMER_QCT_QTIMER_H */