Added basic support for the Cortex A9 Global Timer
authorSebastian Wicki <swicki@student.ethz.ch>
Tue, 4 Feb 2014 23:11:20 +0000 (00:11 +0100)
committerSebastian Wicki <swicki@student.ethz.ch>
Tue, 4 Feb 2014 23:11:20 +0000 (00:11 +0100)
The Cortex A9 global timer is a 64-bit incrementing counter, which
is memory mapped in the private memory region. Thus, the code for
the timer is currently all in the CPU driver.

To read out the timer in userspace, two system calls are needed,
one for the lower and and one for the upper half of the counter.
The following helper function is provided to correctly read out
the timer value in two 32 bit reads as suggested in the ARM TRM:

  errval_t sys_debug_hardware_global_timer_read(uint64_t *ret)

devices/Hakefile
devices/cortex_a9_gt.dev [new file with mode: 0644]
include/barrelfish/sys_debug.h
include/barrelfish_kpi/sys_debug.h
kernel/Hakefile
kernel/arch/armv7/syscall.c
kernel/arch/omap44xx/init.c
kernel/arch/omap44xx/omap.c
kernel/include/arch/armv7/arm_hal.h
lib/barrelfish/arch/arm/sys_debug.c

index d2c7855..f49a78a 100644 (file)
@@ -56,6 +56,7 @@
            "pl130_gic",
            "sp804_pit",
            "cortex_a9_pit",
+           "cortex_a9_gt",
            "a9scu",
            "ti_i2c",
            "ti_twl6030",
diff --git a/devices/cortex_a9_gt.dev b/devices/cortex_a9_gt.dev
new file mode 100644 (file)
index 0000000..b4630b7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+/*
+ * cortex_a9_gt.dev
+ *
+ * DESCRIPTION: Cortex A9 Global Timer
+ *
+ * This is derived from:
+ *
+ * Cortex-A9 MPCore Technical Reference Manual
+ * (DDI0407G_cortex_a9_mpcore_r4p1_trm.pdf)
+ *
+ * This implements private timers and watchdogs
+ */
+ device cortex_a9_gt msbfirst ( addr base ) "Cortex A9 Global Timer" {
+       
+       register TimerCounterLow addr(base, 0x0) "Global Timer Counter Register (Lower Word)" type(uint32);
+       register TimerCounterHigh addr(base, 0x4) "Global Timer Counter Register (Upper Word)" type(uint32);
+       
+       register TimerControl addr(base, 0x8) "Global Timer Control Register" {
+               _                               16      rsvd;
+               prescale                8       rw              "Prescale factor";
+               _                               4       rsvd;
+               auto_increment  1       rw              "Single shot or increment mode";
+               int_enable              1       rw              "Interrupt enable bit";
+               comp_enable             1       rw              "Enable comparision between counter and comparator register";
+               timer_enable    1       rw              "Timer enable bit";
+       };
+       
+       register TimerIntStat addr(base, 0xc) "Global Timer Interrupt Status Register" {
+               _                               31      rsvd;
+               event_flag              1       rw;
+       };
+       
+       register TimerComparatorLow addr(base, 0x10) "Global Timer Comparator Register (Lower Word)" type(uint32);
+       register TimerComparatorHigh addr(base, 0x14) "Global Timer Comparator Register (Upper Word)" type(uint32);
+       
+    register TimerAutoIncrement addr(base, 0x18) "Global Timer Comparator Auto-Increment Register" type(uint32);
+       
+ };
index 16c2482..c41c941 100644 (file)
@@ -40,6 +40,7 @@ errval_t sys_debug_send_ipi(uint8_t destination, uint8_t shorthand, uint8_t vect
 errval_t sys_debug_set_breakpoint(uintptr_t addr, uint8_t mode, uint8_t length);
 errval_t sys_debug_hardware_timer_read(uintptr_t* ret);
 errval_t sys_debug_hardware_timer_hertz_read(uintptr_t* ret);
+errval_t sys_debug_hardware_global_timer_read(uint64_t *ret);
 errval_t sys_debug_get_apic_ticks_per_sec(uint32_t *ret);
 
 #ifdef ENABLE_FEIGN_FRAME_CAP
index 13809fd..c00a8de 100644 (file)
@@ -26,6 +26,8 @@ enum debug_message {
     DEBUG_SLEEP,
     DEBUG_HARDWARE_TIMER_READ,
     DEBUG_HARDWARE_TIMER_HERTZ_READ,
+    DEBUG_HARDWARE_GLOBAL_TIMER_LOW,
+    DEBUG_HARDWARE_GLOBAL_TIMER_HIGH,
     DEBUG_GET_TSC_PER_MS,
     DEBUG_GET_APIC_TIMER,
     DEBUG_GET_APIC_TICKS_PER_SEC,
index 7b1ee48..ed1bc6c 100644 (file)
@@ -364,6 +364,7 @@ let
                          "pl130_gic", 
                          "sp804_pit", 
                          "cortex_a9_pit", 
+                         "cortex_a9_gt",
                          "a9scu", 
                          "omap/omap_uart", 
                          "omap/omap44xx_id", 
index 76f600a..0ffc1ac 100644 (file)
@@ -717,6 +717,14 @@ static struct sysret handle_debug_syscall(int msg)
             retval.value = tsc_get_hz();
             break;
 
+        case DEBUG_HARDWARE_GLOBAL_TIMER_LOW:
+            retval.value = gt_read_low();
+            break;
+
+        case DEBUG_HARDWARE_GLOBAL_TIMER_HIGH:
+            retval.value = gt_read_high();
+            break;
+
         default:
             printk(LOG_ERR, "invalid sys_debug msg type %d\n", msg);
             retval.error = err_push(retval.error, SYS_ERR_ILLEGAL_SYSCALL);
index cfcdc13..e013f8c 100644 (file)
@@ -794,6 +794,7 @@ static void __attribute__ ((noinline,noreturn)) text_init(void)
         }
     }
 
+    gt_init();
     tsc_init();
     printf("tsc_init done --\n");
 #ifndef __gem5__
index 72575e3..eee6af6 100644 (file)
@@ -14,6 +14,7 @@
 #include <dev/sp804_pit_dev.h>
 #include <dev/arm_icp_pit_dev.h>
 #include <dev/cortex_a9_pit_dev.h>
+#include <dev/cortex_a9_gt_dev.h>
 #include <dev/a9scu_dev.h>
 
 #include <arm_hal.h>
@@ -287,6 +288,49 @@ uint32_t tsc_get_hz(void)
     return tsc_hz;
 }
 
+//
+// Cortex A9 Global Timer (see ARM TRM 4.4.1)
+//
+
+#define GT_OFFSET      0x0200
+
+static cortex_a9_gt_t a9_gt;
+
+void gt_init(void)
+{
+    cortex_a9_gt_initialize(&a9_gt, (mackerel_addr_t) private_memory_region + GT_OFFSET);
+
+    // enable auto increment
+    cortex_a9_gt_TimerControl_auto_increment_wrf(&a9_gt, 0x1);
+
+    // reset timer (TRM 4.4.4)
+    cortex_a9_gt_TimerControl_timer_enable_wrf(&a9_gt, 0x0);
+    cortex_a9_gt_TimerCounterLow_wr(&a9_gt, 0x0);
+    cortex_a9_gt_TimerCounterHigh_wr(&a9_gt, 0x0);
+    cortex_a9_gt_TimerControl_timer_enable_wrf(&a9_gt, 0x1);
+}
+
+uint64_t gt_read(void)
+{
+    // need to re-read high value according to ARM TRM 4.4.1
+    uint32_t low, high;
+    do {
+        high = cortex_a9_gt_TimerCounterHigh_rd(&a9_gt);
+        low  = cortex_a9_gt_TimerCounterLow_rd(&a9_gt);
+    } while(high != cortex_a9_gt_TimerCounterHigh_rd(&a9_gt));
+
+    return (((uint64_t) high) << 32) | ((uint32_t) low);
+}
+
+uint32_t gt_read_low(void)
+{
+    return cortex_a9_gt_TimerCounterLow_rd(&a9_gt);
+}
+
+uint32_t gt_read_high(void)
+{
+    return cortex_a9_gt_TimerCounterHigh_rd(&a9_gt);
+}
 
 //
 // Snoop Control Unit
index 261698d..e68914d 100644 (file)
@@ -69,6 +69,14 @@ uint32_t tsc_read(void);
 uint32_t tsc_get_hz(void);
 
 /*
+ * Cortex A9 Global Timer
+ */
+void     gt_init(void);
+uint64_t gt_read(void);
+uint32_t gt_read_low(void);
+uint32_t gt_read_high(void);
+
+/*
  * system control unit
  * only for multi-core
  */
index 37dfeae..e0c9943 100644 (file)
@@ -101,3 +101,23 @@ errval_t sys_debug_hardware_timer_hertz_read(uintptr_t* v)
     *v = sr.value;
     return sr.error;
 }
+
+errval_t sys_debug_hardware_global_timer_read(uint64_t *ret)
+{
+    struct sysret sr;
+
+    uint32_t l, h;
+
+    do {
+        h = syscall2(SYSCALL_DEBUG, DEBUG_HARDWARE_GLOBAL_TIMER_HIGH).value;
+        l = syscall2(SYSCALL_DEBUG, DEBUG_HARDWARE_GLOBAL_TIMER_LOW).value;
+        // read high again, in case it changed
+        sr = syscall2(SYSCALL_DEBUG, DEBUG_HARDWARE_GLOBAL_TIMER_HIGH);
+    } while(h != sr.value && err_is_ok(sr.error));
+
+    if(err_is_ok(sr.error) && ret) {
+        *ret = (((uint64_t) h) << 32) | ((uint32_t) l);
+    }
+
+    return sr.error;
+}