xref: /kvm-unit-tests/arm/timer.c (revision 86943f8a6977a87cea777f20b4ea0890cdf56575)
1 /*
2  * Timer tests for the ARM virt machine.
3  *
4  * Copyright (C) 2017, Alexander Graf <agraf@suse.de>
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2.
7  */
8 #include <libcflat.h>
9 #include <devicetree.h>
10 #include <asm/processor.h>
11 #include <asm/gic.h>
12 #include <asm/io.h>
13 
14 #define ARCH_TIMER_CTL_ENABLE  (1 << 0)
15 #define ARCH_TIMER_CTL_IMASK   (1 << 1)
16 #define ARCH_TIMER_CTL_ISTATUS (1 << 2)
17 
18 static void *gic_ispendr;
19 
20 static u64 read_vtimer_counter(void)
21 {
22 	return read_sysreg(cntvct_el0);
23 }
24 
25 static u64 read_vtimer_cval(void)
26 {
27 	return read_sysreg(cntv_cval_el0);
28 }
29 
30 static void write_vtimer_cval(u64 val)
31 {
32 	write_sysreg(val, cntv_cval_el0);
33 }
34 
35 static u64 read_vtimer_ctl(void)
36 {
37 	return read_sysreg(cntv_ctl_el0);
38 }
39 
40 static void write_vtimer_ctl(u64 val)
41 {
42 	write_sysreg(val, cntv_ctl_el0);
43 }
44 
45 static u64 read_ptimer_counter(void)
46 {
47 	return read_sysreg(cntpct_el0);
48 }
49 
50 static u64 read_ptimer_cval(void)
51 {
52 	return read_sysreg(cntp_cval_el0);
53 }
54 
55 static void write_ptimer_cval(u64 val)
56 {
57 	write_sysreg(val, cntp_cval_el0);
58 }
59 
60 static u64 read_ptimer_ctl(void)
61 {
62 	return read_sysreg(cntp_ctl_el0);
63 }
64 
65 static void write_ptimer_ctl(u64 val)
66 {
67 	write_sysreg(val, cntp_ctl_el0);
68 }
69 
70 struct timer_info {
71 	u32 irq;
72 	u32 irq_flags;
73 	bool irq_received;
74 	u64 (*read_counter)(void);
75 	u64 (*read_cval)(void);
76 	void (*write_cval)(u64);
77 	u64 (*read_ctl)(void);
78 	void (*write_ctl)(u64);
79 };
80 
81 static struct timer_info vtimer_info = {
82 	.irq_received = false,
83 	.read_counter = read_vtimer_counter,
84 	.read_cval = read_vtimer_cval,
85 	.write_cval = write_vtimer_cval,
86 	.read_ctl = read_vtimer_ctl,
87 	.write_ctl = write_vtimer_ctl,
88 };
89 
90 static struct timer_info ptimer_info = {
91 	.irq_received = false,
92 	.read_counter = read_ptimer_counter,
93 	.read_cval = read_ptimer_cval,
94 	.write_cval = write_ptimer_cval,
95 	.read_ctl = read_ptimer_ctl,
96 	.write_ctl = write_ptimer_ctl,
97 };
98 
99 static void set_timer_irq_enabled(struct timer_info *info, bool enabled)
100 {
101 	u32 val = 0;
102 
103 	if (enabled)
104 		val = 1 << PPI(info->irq);
105 
106 	switch (gic_version()) {
107 	case 2:
108 		writel(val, gicv2_dist_base() + GICD_ISENABLER + 0);
109 		break;
110 	case 3:
111 		writel(val, gicv3_sgi_base() + GICR_ISENABLER0);
112 		break;
113 	}
114 }
115 
116 static void irq_handler(struct pt_regs *regs)
117 {
118 	struct timer_info *info;
119 	u32 irqstat = gic_read_iar();
120 	u32 irqnr = gic_iar_irqnr(irqstat);
121 
122 	if (irqnr != GICC_INT_SPURIOUS)
123 		gic_write_eoir(irqstat);
124 
125 	if (irqnr == PPI(vtimer_info.irq)) {
126 		info = &vtimer_info;
127 	} else if (irqnr == PPI(ptimer_info.irq)) {
128 		info = &ptimer_info;
129 	} else {
130 		report_info("Unexpected interrupt: %d\n", irqnr);
131 		return;
132 	}
133 
134 	info->write_ctl(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE);
135 	info->irq_received = true;
136 }
137 
138 static bool gic_timer_pending(struct timer_info *info)
139 {
140 	return readl(gic_ispendr) & (1 << PPI(info->irq));
141 }
142 
143 static bool test_cval_10msec(struct timer_info *info)
144 {
145 	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
146 	u64 time_1us = time_10ms / 10000;
147 	u64 before_timer, after_timer;
148 	s64 difference;
149 
150 	/* Program timer to fire in 10 ms */
151 	before_timer = info->read_counter();
152 	info->write_cval(before_timer + time_10ms);
153 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
154 	isb();
155 
156 	/* Wait for the timer to fire */
157 	while (!(info->read_ctl() & ARCH_TIMER_CTL_ISTATUS))
158 		;
159 
160 	/* It fired, check how long it took */
161 	after_timer = info->read_counter();
162 	difference = after_timer - (before_timer + time_10ms);
163 
164 	report_info("After timer: 0x%016lx", after_timer);
165 	report_info("Expected   : 0x%016lx", before_timer + time_10ms);
166 	report_info("Difference : %ld us", difference / time_1us);
167 
168 	if (difference < 0) {
169 		printf("ISTATUS set too early\n");
170 		return false;
171 	}
172 	return difference < time_10ms;
173 }
174 
175 static void test_timer(struct timer_info *info)
176 {
177 	u64 now = info->read_counter();
178 	u64 time_10s = read_sysreg(cntfrq_el0) * 10;
179 	u64 later = now + time_10s;
180 
181 	/* We don't want the irq handler to fire because that will change the
182 	 * timer state and we want to test the timer output signal.  We can
183 	 * still read the pending state even if it's disabled. */
184 	set_timer_irq_enabled(info, false);
185 
186 	/* Enable the timer, but schedule it for much later */
187 	info->write_cval(later);
188 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
189 	isb();
190 	report("not pending before", !gic_timer_pending(info));
191 
192 	info->write_cval(now - 1);
193 	isb();
194 	report("interrupt signal pending", gic_timer_pending(info));
195 
196 	/* Disable the timer again and prepare to take interrupts */
197 	info->write_ctl(0);
198 	set_timer_irq_enabled(info, true);
199 
200 	report("latency within 10 ms", test_cval_10msec(info));
201 	report("interrupt received", info->irq_received);
202 
203 	/* Disable the timer again */
204 	info->write_ctl(0);
205 }
206 
207 static void test_vtimer(void)
208 {
209 	report_prefix_push("vtimer-busy-loop");
210 	test_timer(&vtimer_info);
211 	report_prefix_pop();
212 }
213 
214 static void test_ptimer(void)
215 {
216 	report_prefix_push("ptimer-busy-loop");
217 	test_timer(&ptimer_info);
218 	report_prefix_pop();
219 }
220 
221 static void test_init(void)
222 {
223 	const struct fdt_property *prop;
224 	const void *fdt = dt_fdt();
225 	int node, len;
226 	u32 *data;
227 
228 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
229 	assert(node >= 0);
230 	prop = fdt_get_property(fdt, node, "interrupts", &len);
231 	assert(prop && len == (4 * 3 * sizeof(u32)));
232 
233 	data = (u32 *)prop->data;
234 	assert(fdt32_to_cpu(data[3]) == 1);
235 	ptimer_info.irq = fdt32_to_cpu(data[4]);
236 	ptimer_info.irq_flags = fdt32_to_cpu(data[5]);
237 	assert(fdt32_to_cpu(data[6]) == 1);
238 	vtimer_info.irq = fdt32_to_cpu(data[7]);
239 	vtimer_info.irq_flags = fdt32_to_cpu(data[8]);
240 
241 	gic_enable_defaults();
242 
243 	switch (gic_version()) {
244 	case 2:
245 		gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
246 		break;
247 	case 3:
248 		gic_ispendr = gicv3_sgi_base() + GICD_ISPENDR;
249 		break;
250 	}
251 
252 	install_irq_handler(EL1H_IRQ, irq_handler);
253 	local_irq_enable();
254 }
255 
256 static void print_vtimer_info(void)
257 {
258 	printf("CNTFRQ_EL0   : 0x%016lx\n", read_sysreg(cntfrq_el0));
259 	printf("CNTVCT_EL0   : 0x%016lx\n", read_sysreg(cntvct_el0));
260 	printf("CNTV_CTL_EL0 : 0x%016lx\n", read_sysreg(cntv_ctl_el0));
261 	printf("CNTV_CVAL_EL0: 0x%016lx\n", read_sysreg(cntv_cval_el0));
262 }
263 
264 static void print_ptimer_info(void)
265 {
266 	printf("CNTPCT_EL0   : 0x%016lx\n", read_sysreg(cntpct_el0));
267 	printf("CNTP_CTL_EL0 : 0x%016lx\n", read_sysreg(cntp_ctl_el0));
268 	printf("CNTP_CVAL_EL0: 0x%016lx\n", read_sysreg(cntp_cval_el0));
269 }
270 
271 
272 int main(int argc, char **argv)
273 {
274 	bool run_ptimer_test = false;
275 	bool run_vtimer_test = false;
276 
277 	/* Check if we should also check the physical timer */
278 	if (argc > 1) {
279 		if (strcmp(argv[1], "vtimer") == 0) {
280 			run_vtimer_test = true;
281 		} else if (strcmp(argv[1], "ptimer") == 0) {
282 			run_ptimer_test = true;
283 		} else {
284 			report_abort("Unknown option '%s'", argv[1]);
285 		}
286 	} else {
287 		run_vtimer_test = true;
288 	}
289 
290 	if (run_vtimer_test)
291 		print_vtimer_info();
292 	else if (run_ptimer_test)
293 		print_ptimer_info();
294 
295 	test_init();
296 
297 	if (run_vtimer_test)
298 		test_vtimer();
299 	else if (run_ptimer_test)
300 		test_ptimer();
301 
302 
303 	return report_summary();
304 }
305