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