@@ -11,34 +11,119 @@
#include <asm/gic.h>
#include <asm/io.h>
-#define CNTV_CTL_ENABLE (1 << 0)
-#define CNTV_CTL_IMASK (1 << 1)
-#define CNTV_CTL_ISTATUS (1 << 2)
+#define ARCH_TIMER_CTL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTL_IMASK (1 << 1)
+#define ARCH_TIMER_CTL_ISTATUS (1 << 2)
-static u32 vtimer_irq, vtimer_irq_flags;
static void *gic_ispendr;
-static bool vtimer_irq_received;
+
+static u64 read_vtimer_counter(void)
+{
+ return read_sysreg(cntvct_el0);
+}
+
+static u64 read_vtimer_cval(void)
+{
+ return read_sysreg(cntv_cval_el0);
+}
+
+static void write_vtimer_cval(u64 val)
+{
+ write_sysreg(val, cntv_cval_el0);
+}
+
+static u64 read_vtimer_ctl(void)
+{
+ return read_sysreg(cntv_ctl_el0);
+}
+
+static void write_vtimer_ctl(u64 val)
+{
+ write_sysreg(val, cntv_ctl_el0);
+}
+
+static u64 read_ptimer_counter(void)
+{
+ return read_sysreg(cntpct_el0);
+}
+
+static u64 read_ptimer_cval(void)
+{
+ return read_sysreg(cntp_cval_el0);
+}
+
+static void write_ptimer_cval(u64 val)
+{
+ write_sysreg(val, cntp_cval_el0);
+}
+
+static u64 read_ptimer_ctl(void)
+{
+ return read_sysreg(cntp_ctl_el0);
+}
+
+static void write_ptimer_ctl(u64 val)
+{
+ write_sysreg(val, cntp_ctl_el0);
+}
+
+struct timer_info {
+ u32 irq;
+ u32 irq_flags;
+ bool irq_received;
+ u64 (*read_counter)(void);
+ u64 (*read_cval)(void);
+ void (*write_cval)(u64);
+ u64 (*read_ctl)(void);
+ void (*write_ctl)(u64);
+};
+
+static struct timer_info vtimer_info = {
+ .irq_received = false,
+ .read_counter = read_vtimer_counter,
+ .read_cval = read_vtimer_cval,
+ .write_cval = write_vtimer_cval,
+ .read_ctl = read_vtimer_ctl,
+ .write_ctl = write_vtimer_ctl,
+};
+
+static struct timer_info ptimer_info = {
+ .irq_received = false,
+ .read_counter = read_ptimer_counter,
+ .read_cval = read_ptimer_cval,
+ .write_cval = write_ptimer_cval,
+ .read_ctl = read_ptimer_ctl,
+ .write_ctl = write_ptimer_ctl,
+};
static void irq_handler(struct pt_regs *regs)
{
+ struct timer_info *info;
u32 irqstat = gic_read_iar();
u32 irqnr = gic_iar_irqnr(irqstat);
if (irqnr != GICC_INT_SPURIOUS)
gic_write_eoir(irqstat);
- if (irqnr == PPI(vtimer_irq)) {
- write_sysreg(CNTV_CTL_IMASK | CNTV_CTL_ENABLE, cntv_ctl_el0);
- ++vtimer_irq_received;
+ if (irqnr == PPI(vtimer_info.irq)) {
+ info = &vtimer_info;
+ } else if (irqnr == PPI(ptimer_info.irq)) {
+ info = &ptimer_info;
+ } else {
+ report_info("Unexpected interrupt: %d\n", irqnr);
+ return;
}
+
+ info->write_ctl(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE);
+ info->irq_received = true;
}
-static bool gic_vtimer_pending(void)
+static bool gic_timer_pending(struct timer_info *info)
{
- return readl(gic_ispendr) & (1 << PPI(vtimer_irq));
+ return readl(gic_ispendr) & (1 << PPI(info->irq));
}
-static bool test_cval_10msec(void)
+static bool test_cval_10msec(struct timer_info *info)
{
u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
u64 time_1us = time_10ms / 10000;
@@ -46,15 +131,15 @@ static bool test_cval_10msec(void)
s64 difference;
/* Program timer to fire in 10 ms */
- before_timer = read_sysreg(cntvct_el0);
- write_sysreg(before_timer + time_10ms, cntv_cval_el0);
+ before_timer = info->read_counter();
+ info->write_cval(before_timer + time_10ms);
/* Wait for the timer to fire */
- while (!(read_sysreg(cntv_ctl_el0) & CNTV_CTL_ISTATUS))
+ while (!(info->read_ctl() & ARCH_TIMER_CTL_ISTATUS))
;
/* It fired, check how long it took */
- after_timer = read_sysreg(cntvct_el0);
+ after_timer = info->read_counter();
difference = after_timer - (before_timer + time_10ms);
report_info("After timer: 0x%016lx", after_timer);
@@ -62,32 +147,43 @@ static bool test_cval_10msec(void)
report_info("Difference : %ld us", difference / time_1us);
if (difference < 0) {
- printf("CNTV_CTL_EL0.ISTATUS set too early\n");
+ printf("ISTATUS set too early\n");
return false;
}
return difference < time_10ms;
}
-static void test_vtimer(void)
+static void test_timer(struct timer_info *info)
{
u64 now = read_sysreg(cntvct_el0);
u64 time_10s = read_sysreg(cntfrq_el0) * 10;
u64 later = now + time_10s;
- report_prefix_push("vtimer-busy-loop");
/* Enable the timer, but schedule it for much later*/
- write_sysreg(later, cntv_cval_el0);
+ info->write_cval(later);
isb();
- write_sysreg(CNTV_CTL_ENABLE, cntv_ctl_el0);
+ info->write_ctl(ARCH_TIMER_CTL_ENABLE);
- report("not pending before", !gic_vtimer_pending());
- report("latency within 10 ms", test_cval_10msec());
- report("interrupt received", vtimer_irq_received);
+ report("not pending before", !gic_timer_pending(info));
+ report("latency within 10 ms", test_cval_10msec(info));
+ report("interrupt received", info->irq_received);
/* Disable the timer again */
- write_sysreg(0, cntv_ctl_el0);
+ info->write_ctl(0);
+}
+
+static void test_vtimer(void)
+{
+ report_prefix_push("vtimer-busy-loop");
+ test_timer(&vtimer_info);
+ report_prefix_pop();
+}
+static void test_ptimer(void)
+{
+ report_prefix_push("ptimer-busy-loop");
+ test_timer(&ptimer_info);
report_prefix_pop();
}
@@ -102,20 +198,26 @@ static void test_init(void)
assert(node >= 0);
prop = fdt_get_property(fdt, node, "interrupts", &len);
assert(prop && len == (4 * 3 * sizeof(u32)));
+
data = (u32 *)prop->data;
+ assert(fdt32_to_cpu(data[3]) == 1);
+ ptimer_info.irq = fdt32_to_cpu(data[4]);
+ ptimer_info.irq_flags = fdt32_to_cpu(data[5]);
assert(fdt32_to_cpu(data[6]) == 1);
- vtimer_irq = fdt32_to_cpu(data[7]);
- vtimer_irq_flags = fdt32_to_cpu(data[8]);
+ vtimer_info.irq = fdt32_to_cpu(data[7]);
+ vtimer_info.irq_flags = fdt32_to_cpu(data[8]);
gic_enable_defaults();
switch (gic_version()) {
case 2:
- writel(1 << PPI(vtimer_irq), gicv2_dist_base() + GICD_ISENABLER + 0);
+ writel(1 << PPI(vtimer_info.irq), gicv2_dist_base() + GICD_ISENABLER + 0);
+ writel(1 << PPI(ptimer_info.irq), gicv2_dist_base() + GICD_ISENABLER + 0);
gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
break;
case 3:
- writel(1 << PPI(vtimer_irq), gicv3_sgi_base() + GICR_ISENABLER0);
+ writel(1 << PPI(vtimer_info.irq), gicv3_sgi_base() + GICR_ISENABLER0);
+ writel(1 << PPI(ptimer_info.irq), gicv3_sgi_base() + GICR_ISENABLER0);
gic_ispendr = gicv3_sgi_base() + GICD_ISPENDR;
break;
}
@@ -124,15 +226,52 @@ static void test_init(void)
local_irq_enable();
}
-int main(void)
+static void print_vtimer_info(void)
{
printf("CNTFRQ_EL0 : 0x%016lx\n", read_sysreg(cntfrq_el0));
printf("CNTVCT_EL0 : 0x%016lx\n", read_sysreg(cntvct_el0));
printf("CNTV_CTL_EL0 : 0x%016lx\n", read_sysreg(cntv_ctl_el0));
printf("CNTV_CVAL_EL0: 0x%016lx\n", read_sysreg(cntv_cval_el0));
+}
+
+static void print_ptimer_info(void)
+{
+ printf("CNTPCT_EL0 : 0x%016lx\n", read_sysreg(cntpct_el0));
+ printf("CNTP_CTL_EL0 : 0x%016lx\n", read_sysreg(cntp_ctl_el0));
+ printf("CNTP_CVAL_EL0: 0x%016lx\n", read_sysreg(cntp_cval_el0));
+}
+
+
+int main(int argc, char **argv)
+{
+ bool run_ptimer_test = false;
+ bool run_vtimer_test = false;
+
+ /* Check if we should also check the physical timer */
+ if (argc > 1) {
+ if (strcmp(argv[1], "vtimer") == 0) {
+ run_vtimer_test = true;
+ } else if (strcmp(argv[1], "ptimer") == 0) {
+ run_ptimer_test = true;
+ } else {
+ report_abort("Unknown option '%s'", argv[1]);
+ }
+ } else {
+ run_vtimer_test = true;
+ }
+
+ if (run_vtimer_test)
+ print_vtimer_info();
+ else if (run_ptimer_test)
+ print_ptimer_info();
test_init();
- test_vtimer();
+
+ if (run_vtimer_test)
+ test_vtimer();
+ else if (run_ptimer_test)
+ test_ptimer();
+
return report_summary();
}
@@ -111,7 +111,14 @@ smp = $MAX_SMP
groups = psci
# Timer tests
-[timer]
+[vtimer]
file = timer.flat
+extra_params = -append 'vtimer'
+groups = timer
+timeout = 2s
+
+[ptimer]
+file = timer.flat
+extra_params = -append 'ptimer'
groups = timer
timeout = 2s
Rearrange the code to be able to reuse as much as posible and add support for testing the physical timer as well. Also change the default unittests configuration to run a separate vtimer and ptimer test so that older kernels without ptimer support just show a failure. Signed-off-by: Christoffer Dall <cdall@linaro.org> --- arm/timer.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++-------- arm/unittests.cfg | 9 ++- 2 files changed, 177 insertions(+), 31 deletions(-)