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