xref: /kvm-unit-tests/arm/timer.c (revision 310d1af796124a44919090155b332fbbda6efbc4)
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 CNTV_CTL_ENABLE  (1 << 0)
15 #define CNTV_CTL_IMASK   (1 << 1)
16 #define CNTV_CTL_ISTATUS (1 << 2)
17 
18 static u32 vtimer_irq, vtimer_irq_flags;
19 static void *gic_ispendr;
20 static bool vtimer_irq_received;
21 
22 static void irq_handler(struct pt_regs *regs)
23 {
24 	u32 irqstat = gic_read_iar();
25 	u32 irqnr = gic_iar_irqnr(irqstat);
26 
27 	if (irqnr != GICC_INT_SPURIOUS)
28 		gic_write_eoir(irqstat);
29 
30 	if (irqnr == PPI(vtimer_irq)) {
31 		write_sysreg(CNTV_CTL_IMASK, cntv_ctl_el0);
32 		++vtimer_irq_received;
33 	}
34 }
35 
36 static bool gic_vtimer_pending(void)
37 {
38 	return readl(gic_ispendr) & (1 << PPI(vtimer_irq));
39 }
40 
41 static bool test_cval_10msec(void)
42 {
43 	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
44 	u64 time_1us = time_10ms / 10000;
45 	u64 before_timer, after_timer;
46 	s64 difference;
47 
48 	/* Program timer to fire in 10 ms */
49 	before_timer = read_sysreg(cntvct_el0);
50 	write_sysreg(before_timer + time_10ms, cntv_cval_el0);
51 
52 	/* Wait for the timer to fire */
53 	while (!(read_sysreg(cntv_ctl_el0) & CNTV_CTL_ISTATUS))
54 		;
55 
56 	/* It fired, check how long it took */
57 	after_timer = read_sysreg(cntvct_el0);
58 	difference = after_timer - (before_timer + time_10ms);
59 
60 	report_info("After timer: 0x%016lx", after_timer);
61 	report_info("Expected   : 0x%016lx", before_timer + time_10ms);
62 	report_info("Difference : %ld us", difference / time_1us);
63 
64 	if (difference < 0) {
65 		printf("CNTV_CTL_EL0.ISTATUS set too early\n");
66 		return false;
67 	}
68 	return difference < time_10ms;
69 }
70 
71 static void test_vtimer(void)
72 {
73 	report_prefix_push("vtimer-busy-loop");
74 
75 	/* Enable the timer */
76 	write_sysreg(~0, cntv_cval_el0);
77 	isb();
78 	write_sysreg(CNTV_CTL_ENABLE, cntv_ctl_el0);
79 
80 	report("not pending before", !gic_vtimer_pending());
81 	report("latency within 10 ms", test_cval_10msec());
82 	report("interrupt received", vtimer_irq_received);
83 
84 	/* Disable the timer again */
85 	write_sysreg(0, cntv_ctl_el0);
86 
87 	report_prefix_pop();
88 }
89 
90 static void test_init(void)
91 {
92 	const struct fdt_property *prop;
93 	const void *fdt = dt_fdt();
94 	int node, len;
95 	u32 *data;
96 
97 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
98 	assert(node >= 0);
99 	prop = fdt_get_property(fdt, node, "interrupts", &len);
100 	assert(prop && len == (4 * 3 * sizeof(u32)));
101 	data = (u32 *)prop->data;
102 	assert(fdt32_to_cpu(data[6]) == 1);
103 	vtimer_irq = fdt32_to_cpu(data[7]);
104 	vtimer_irq_flags = fdt32_to_cpu(data[8]);
105 
106 	gic_enable_defaults();
107 
108 	switch (gic_version()) {
109 	case 2:
110 		writel(1 << PPI(vtimer_irq), gicv2_dist_base() + GICD_ISENABLER + 0);
111 		gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
112 		break;
113 	case 3:
114 		writel(1 << PPI(vtimer_irq), gicv3_sgi_base() + GICR_ISENABLER0);
115 		gic_ispendr = gicv3_sgi_base() + GICD_ISPENDR;
116 		break;
117 	}
118 
119 	install_irq_handler(EL1H_IRQ, irq_handler);
120 	local_irq_enable();
121 }
122 
123 int main(void)
124 {
125 	printf("CNTFRQ_EL0   : 0x%016lx\n", read_sysreg(cntfrq_el0));
126 	printf("CNTVCT_EL0   : 0x%016lx\n", read_sysreg(cntvct_el0));
127 	printf("CNTV_CTL_EL0 : 0x%016lx\n", read_sysreg(cntv_ctl_el0));
128 	printf("CNTV_CVAL_EL0: 0x%016lx\n", read_sysreg(cntv_cval_el0));
129 
130 	test_init();
131 	test_vtimer();
132 
133 	return report_summary();
134 }
135