@samitouri / QOSamiQemu / commits / 71d618d3aa

tests/qtest: add qct-qtimer qtest

Add a qtest exercising the QCT QTimer's register access, view-region frame addressing, and one-shot timer firing behavior. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-18-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 71d618d3aa64bd7a33d8fc14fff6f3051ad633c2
3 files changed +387 -1
MAINTAINERS
+1
@@ -255,6 +255,7 @@ 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 +F: tests/qtest/qct-qtimer-test.c
259 X: target/hexagon/idef-parser/
260 X: target/hexagon/gen_idef_parser_funcs.py
261 F: linux-user/hexagon/
tests/qtest/meson.build
+1 -1
@@ -299,7 +299,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \
299 ['iommu-riscv-test'] : []) + \
300 (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : [])
301
302 -qtests_hexagon = ['boot-serial-test', 'l2vic-test']
302 +qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test']
303
304 qos_test_ss = ss.source_set()
305 qos_test_ss.add(
tests/qtest/qct-qtimer-test.c new
+385
@@ -0,0 +1,385 @@
1 +/*
2 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3 + * SPDX-License-Identifier: GPL-2.0-or-later
4 + *
5 + * QTest testcase for the QCT QTimer
6 + */
7 +
8 +#include "qemu/osdep.h"
9 +#include "libqtest-single.h"
10 +#include "hw/hexagon/hexagon.h"
11 +#include "qemu/bitops.h"
12 +
13 +#include "hw/hexagon/machine_cfg_v68n_1024.h.inc"
14 +#include "hw/hexagon/machine_cfg_v66g_1024.h.inc"
15 +
16 +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL
17 +
18 +#define QCT_QTIMER_CNTPCT_LO (0x000)
19 +#define QCT_QTIMER_CNTPCT_HI (0x004)
20 +#define QCT_QTIMER_CNT_FREQ (0x010)
21 +#define QCT_QTIMER_CNTP_CVAL_LO (0x020)
22 +#define QCT_QTIMER_CNTP_TVAL (0x028)
23 +#define QCT_QTIMER_CNTP_CTL (0x02c)
24 +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0)
25 +
26 +#define QTIMER_FRAME_STRIDE 0x1000
27 +/* Frames instantiated by hex-subsys, and the L2VIC input frame 0 drives. */
28 +#define QTIMER_NR_FRAMES 3
29 +#define QTIMER_L2VIC_IRQ_BASE 2
30 +
31 +#define QCT_QTIMER_AC_CNTFRQ (0x000)
32 +#define QCT_QTIMER_AC_CNTSR (0x004)
33 +#define QCT_QTIMER_AC_CNTTID_0 (0x08)
34 +#define QCT_QTIMER_AC_CNTACR_START (0x40)
35 +#define QCT_QTIMER_AC_CNTACR_ALL (0x3f)
36 +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */
37 +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */
38 +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */
39 +
40 +static uint64_t qtimer_view_base;
41 +static uint64_t qtimer_ac_base;
42 +
43 +#define TIMER_TEST_OFFSET 1000
44 +/* TIMER_TEST_OFFSET ticks expressed in nanoseconds of QEMU_CLOCK_VIRTUAL */
45 +#define TIMER_TEST_NS \
46 + ((TIMER_TEST_OFFSET * 1000000000ULL) / QTIMER_DEFAULT_FREQ_HZ)
47 +
48 +static uint32_t qtimer_read32(uint64_t base, uint32_t offset)
49 +{
50 + return readl(base + offset);
51 +}
52 +
53 +static void qtimer_write32(uint64_t base, uint32_t offset, uint32_t value)
54 +{
55 + writel(base + offset, value);
56 +}
57 +
58 +static uint64_t qtimer_read64(uint64_t base, uint32_t offset)
59 +{
60 + uint32_t lo = qtimer_read32(base, offset);
61 + uint32_t hi = qtimer_read32(base, offset + 4);
62 +
63 + return ((uint64_t)hi << 32) | lo;
64 +}
65 +
66 +static void qtimer_write64(uint64_t base, uint32_t offset, uint64_t value)
67 +{
68 + qtimer_write32(base, offset, extract64(value, 0, 32));
69 + qtimer_write32(base, offset + 4, extract64(value, 32, 32));
70 +}
71 +
72 +static void test_qtimer_basic_access(void)
73 +{
74 + uint32_t val;
75 +
76 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ);
77 + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ);
78 +}
79 +
80 +static void test_qtimer_multiple_frames(void)
81 +{
82 + uint32_t val;
83 + uint64_t frame0_base = qtimer_view_base;
84 + uint64_t frame1_base = qtimer_view_base + 0x1000;
85 +
86 + val = qtimer_read32(frame0_base, QCT_QTIMER_CNT_FREQ);
87 + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ);
88 +
89 + val = qtimer_read32(frame1_base, QCT_QTIMER_CNT_FREQ);
90 + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ);
91 +}
92 +
93 +static void test_qtimer_register_reads(void)
94 +{
95 + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ);
96 + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
97 + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
98 + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
99 + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL);
100 +}
101 +
102 +static void test_qtimer_control_registers(void)
103 +{
104 + uint32_t ctl_val;
105 + uint64_t cval_before, cval_after;
106 +
107 + cval_before = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
108 +
109 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 1000);
110 +
111 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1);
112 + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
113 + g_assert_cmpuint(ctl_val & 1, ==, 1);
114 +
115 + /* CVAL should be greater than before since we set TVAL */
116 + cval_after = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
117 + g_assert_cmpuint(cval_after, >, cval_before);
118 +
119 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0);
120 + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
121 + g_assert_cmpuint(ctl_val & 1, ==, 0);
122 +}
123 +
124 +static void test_qtimer_cval_access(void)
125 +{
126 + uint64_t current_time, test_cval, read_cval;
127 +
128 + current_time = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
129 + test_cval = current_time + 10000;
130 +
131 + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, test_cval);
132 + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
133 + g_assert_cmpuint(read_cval, ==, test_cval);
134 +}
135 +
136 +static void test_qtimer_counter_progression(void)
137 +{
138 + uint32_t freq;
139 + uint64_t count1, count2;
140 +
141 + /*
142 + * In qtest mode the virtual clock does not advance on its own, so
143 + * reading the counter twice must give the same value.
144 + */
145 + count1 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
146 + count2 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
147 + g_assert_cmpuint(count2, ==, count1);
148 +
149 + freq = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ);
150 + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ);
151 +}
152 +
153 +static void test_qtimer_timer_behavior(void)
154 +{
155 + uint64_t current_count, target_count, read_cval, new_count;
156 + uint64_t ctl_val, count_after_disable;
157 +
158 + current_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
159 +
160 + target_count = current_count + TIMER_TEST_OFFSET;
161 + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, target_count);
162 +
163 + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
164 + g_assert_cmpuint(read_cval, ==, target_count);
165 +
166 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1);
167 +
168 + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
169 + /* EN set, IMASK clear, ISTAT not yet pending */
170 + g_assert_cmpuint(ctl_val, ==, 0x1);
171 +
172 + /* Step forward but not past the target */
173 + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2);
174 + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
175 + g_assert_cmpuint(new_count, >=, current_count);
176 +
177 + /* Step past the target */
178 + qtest_clock_step(global_qtest, TIMER_TEST_NS);
179 + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
180 + g_assert_cmpuint(new_count, >=, target_count);
181 +
182 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0);
183 +
184 + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
185 + /* EN cleared, ISTAT set since new_count >= target_count */
186 + g_assert_cmpuint(ctl_val, ==, 0x4);
187 +
188 + /*
189 + * CNTPCT runs independently of CNTP_CTL.EN: only the compare/IRQ
190 + * logic is gated by EN, so the counter must keep advancing.
191 + */
192 + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2);
193 + count_after_disable = qtimer_read64(qtimer_view_base,
194 + QCT_QTIMER_CNTPCT_LO);
195 + g_assert_cmpuint(count_after_disable, >, new_count);
196 +
197 + /* ISTAT remains set while CNTPCT >= CVAL, even with EN=0 */
198 + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
199 + g_assert_cmpuint(ctl_val, ==, 0x4);
200 +}
201 +
202 +/* Test the access-control region: CNTFRQ, CNTSR, CNTTID_0, CNTACR frame 0 */
203 +static void test_qtimer_ac_region(void)
204 +{
205 + uint32_t freq, sr, tid0, acr0;
206 +
207 + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ);
208 + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ);
209 +
210 + /* A write of 0 to CNTFRQ must be ignored (freq-hz must stay nonzero). */
211 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ, 0);
212 + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ);
213 + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ);
214 +
215 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR, 0x3);
216 + sr = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR);
217 + g_assert_cmpuint(sr, ==, 0x3);
218 +
219 + tid0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTTID_0);
220 + g_assert_cmpuint(tid0, ==, 0x111);
221 +
222 + /* CNTACR for frame 0 defaults to full read/write permissions. */
223 + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START);
224 + g_assert_cmpuint(acr0, !=, 0);
225 +
226 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, 0);
227 + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START);
228 + g_assert_cmpuint(acr0, ==, 0);
229 +
230 + /* Restore full permissions so later view-region tests keep working. */
231 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START,
232 + QCT_QTIMER_AC_CNTACR_ALL);
233 +}
234 +
235 +static void test_qtimer_access_denied(void)
236 +{
237 + uint32_t acr_all = QCT_QTIMER_AC_CNTACR_ALL;
238 + uint64_t cval, saved_cval;
239 + uint32_t val;
240 +
241 + /* Park CVAL at a known nonzero value while access is still permitted. */
242 + saved_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO) + 10000;
243 + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, saved_cval);
244 + g_assert_cmpuint(qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO),
245 + ==, saved_cval);
246 +
247 + /* Clearing RFRQ denies CNTFRQ reads. */
248 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START,
249 + acr_all & ~QCT_QTIMER_AC_CNTACR_RFRQ);
250 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ);
251 + g_assert_cmpuint(val, ==, 0);
252 +
253 + /* Clearing RPCT denies CNTPCT reads, both halves. */
254 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START,
255 + acr_all & ~QCT_QTIMER_AC_CNTACR_RPCT);
256 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_LO);
257 + g_assert_cmpuint(val, ==, 0);
258 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_HI);
259 + g_assert_cmpuint(val, ==, 0);
260 +
261 + /*
262 + * Clearing RWPT denies the whole CNTP_* set: CVAL/TVAL/CTL reads all
263 + * read back 0 even though CVAL holds saved_cval.
264 + */
265 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START,
266 + acr_all & ~QCT_QTIMER_AC_CNTACR_RWPT);
267 + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
268 + g_assert_cmpuint(cval, ==, 0);
269 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL);
270 + g_assert_cmpuint(val, ==, 0);
271 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
272 + g_assert_cmpuint(val, ==, 0);
273 +
274 + /* Denied writes must be dropped rather than applied. */
275 + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, 0x1234);
276 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 0x5678);
277 + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1);
278 +
279 + /* Restoring RWPT reveals that CVAL still holds the pre-denial value. */
280 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all);
281 + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO);
282 + g_assert_cmpuint(cval, ==, saved_cval);
283 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL);
284 + g_assert_cmpuint(val & 1, ==, 0);
285 +
286 + /* CNTFRQ and CNTPCT work again once permissions are back. */
287 + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ);
288 + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ);
289 +
290 + /*
291 + * An unimplemented offset in the view region is also an access error,
292 + * and reads back as 0.
293 + */
294 + val = qtimer_read32(qtimer_view_base, 0x100);
295 + g_assert_cmpuint(val, ==, 0);
296 +
297 + /* Leave the frame with full permissions for any later test. */
298 + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all);
299 +}
300 +
301 +static void test_qtimer_frame_irq_routing(void)
302 +{
303 + unsigned int frame, other;
304 + uint64_t frame_base;
305 +
306 + qtest_irq_intercept_in(global_qtest, "/machine/l2vic");
307 +
308 + for (frame = 0; frame < QTIMER_NR_FRAMES; frame++) {
309 + frame_base = qtimer_view_base + frame * QTIMER_FRAME_STRIDE;
310 +
311 + qtimer_write32(frame_base, QCT_QTIMER_CNTP_TVAL, TIMER_TEST_OFFSET);
312 + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL,
313 + QCT_QTIMER_CNTP_CTL_ENABLE);
314 + g_assert_false(qtest_get_irq(global_qtest,
315 + QTIMER_L2VIC_IRQ_BASE + frame));
316 +
317 + /* Step past the deadline so the frame raises its interrupt. */
318 + qtest_clock_step(global_qtest, TIMER_TEST_NS * 2);
319 + g_assert_true(qtest_get_irq(global_qtest,
320 + QTIMER_L2VIC_IRQ_BASE + frame));
321 +
322 + /* No other frame's line may be disturbed. */
323 + for (other = 0; other < QTIMER_NR_FRAMES; other++) {
324 + if (other != frame) {
325 + g_assert_false(qtest_get_irq(global_qtest,
326 + QTIMER_L2VIC_IRQ_BASE + other));
327 + }
328 + }
329 +
330 + /* Clearing EN drops the interrupt, leaving a clean slate. */
331 + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL, 0);
332 + g_assert_false(qtest_get_irq(global_qtest,
333 + QTIMER_L2VIC_IRQ_BASE + frame));
334 + }
335 +}
336 +
337 +typedef struct {
338 + const char *machine;
339 + const struct hexagon_machine_config *cfg;
340 +} QtimerMachineCfg;
341 +
342 +static const QtimerMachineCfg qtimer_machines[] = {
343 + { "virt", &v68n_1024 },
344 + { "V66G_1024", &v66g_1024 },
345 +};
346 +
347 +static void test_qtimer_on_machine(gconstpointer data)
348 +{
349 + const QtimerMachineCfg *mc = data;
350 + g_autofree char *args = g_strdup_printf(
351 + "-machine %s -global qct-qtimer.freq-scale=1", mc->machine);
352 +
353 + qtimer_view_base = mc->cfg->qtmr_region;
354 + qtimer_ac_base = mc->cfg->csr_base;
355 +
356 + qtest_start(args);
357 +
358 + test_qtimer_basic_access();
359 + test_qtimer_multiple_frames();
360 + test_qtimer_register_reads();
361 + test_qtimer_control_registers();
362 + test_qtimer_cval_access();
363 + test_qtimer_counter_progression();
364 + test_qtimer_timer_behavior();
365 + test_qtimer_ac_region();
366 + test_qtimer_access_denied();
367 + test_qtimer_frame_irq_routing();
368 +
369 + qtest_end();
370 +}
371 +
372 +int main(int argc, char **argv)
373 +{
374 + size_t i;
375 +
376 + g_test_init(&argc, &argv, NULL);
377 +
378 + for (i = 0; i < ARRAY_SIZE(qtimer_machines); i++) {
379 + g_autofree char *path = g_strdup_printf("/qct-qtimer/%s/all-tests",
380 + qtimer_machines[i].machine);
381 + qtest_add_data_func(path, &qtimer_machines[i], test_qtimer_on_machine);
382 + }
383 +
384 + return g_test_run();
385 +}