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