@samitouri / QOSamiQemu / commits / 954f0cec38

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, &reg_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, &reg_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 */