Use secure timer to generate S-EL1 interrupts
This patch adds support in the TSP to program the secure physical
generic timer to generate a EL-1 interrupt every half second. It also
adds support for maintaining the timer state across power management
operations. The TSPD ensures that S-EL1 can access the timer by
programming the SCR_EL3.ST bit.
This patch does not actually enable the timer. This will be done in a
subsequent patch once the complete framework for handling S-EL1
interrupts is in place.
Change-Id: I1b3985cfb50262f60824be3a51c6314ce90571bc
diff --git a/bl32/tsp/tsp.mk b/bl32/tsp/tsp.mk
index c478b43..07bd9c6 100644
--- a/bl32/tsp/tsp.mk
+++ b/bl32/tsp/tsp.mk
@@ -32,7 +32,8 @@
bl32/tsp/aarch64/tsp_entrypoint.S \
bl32/tsp/aarch64/tsp_request.S \
common/aarch64/early_exceptions.S \
- lib/locks/exclusive/spinlock.S
+ lib/locks/exclusive/spinlock.S \
+ bl32/tsp/tsp_timer.c
BL32_LINKERFILE := bl32/tsp/tsp.ld.S
diff --git a/bl32/tsp/tsp_timer.c b/bl32/tsp/tsp_timer.c
new file mode 100644
index 0000000..f66ff9f
--- /dev/null
+++ b/bl32/tsp/tsp_timer.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <arch_helpers.h>
+#include <assert.h>
+#include <tsp.h>
+
+/*******************************************************************************
+ * Data structure to keep track of per-cpu secure generic timer context across
+ * power management operations.
+ ******************************************************************************/
+typedef struct timer_context {
+ uint64_t cval;
+ uint32_t ctl;
+} timer_context_t;
+
+static timer_context_t pcpu_timer_context[PLATFORM_CORE_COUNT];
+
+/*******************************************************************************
+ * This function initializes the generic timer to fire every 0.5 second
+ ******************************************************************************/
+void tsp_generic_timer_start()
+{
+ uint64_t cval;
+ uint32_t ctl = 0;
+
+ /* The timer will fire every 0.5 second */
+ cval = read_cntpct_el0() + (read_cntfrq_el0() >> 1);
+ write_cntps_cval_el1(cval);
+
+ /* Enable the secure physical timer */
+ set_cntp_ctl_enable(ctl);
+ write_cntps_ctl_el1(ctl);
+}
+
+/*******************************************************************************
+ * This function deasserts the timer interrupt and sets it up again
+ ******************************************************************************/
+void tsp_generic_timer_handler()
+{
+ /* Ensure that the timer did assert the interrupt */
+ assert(get_cntp_ctl_istatus(read_cntps_ctl_el1()));
+
+ /* Disable the timer and reprogram it */
+ write_cntps_ctl_el1(0);
+ tsp_generic_timer_start();
+}
+
+/*******************************************************************************
+ * This function deasserts the timer interrupt prior to cpu power down
+ ******************************************************************************/
+void tsp_generic_timer_stop()
+{
+ /* Disable the timer */
+ write_cntps_ctl_el1(0);
+}
+
+/*******************************************************************************
+ * This function saves the timer context prior to cpu suspension
+ ******************************************************************************/
+void tsp_generic_timer_save()
+{
+ uint32_t linear_id = platform_get_core_pos(read_mpidr());
+
+ pcpu_timer_context[linear_id].cval = read_cntps_cval_el1();
+ pcpu_timer_context[linear_id].ctl = read_cntps_ctl_el1();
+ flush_dcache_range((uint64_t) &pcpu_timer_context[linear_id],
+ sizeof(pcpu_timer_context[linear_id]));
+}
+
+/*******************************************************************************
+ * This function restores the timer context post cpu resummption
+ ******************************************************************************/
+void tsp_generic_timer_restore()
+{
+ uint32_t linear_id = platform_get_core_pos(read_mpidr());
+
+ write_cntps_cval_el1(pcpu_timer_context[linear_id].cval);
+ write_cntps_ctl_el1(pcpu_timer_context[linear_id].ctl);
+}
diff --git a/include/bl32/payloads/tsp.h b/include/bl32/payloads/tsp.h
index 1f542d5..385d09c 100644
--- a/include/bl32/payloads/tsp.h
+++ b/include/bl32/payloads/tsp.h
@@ -196,6 +196,13 @@
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
+
+/* Generic Timer functions */
+extern void tsp_generic_timer_start(void);
+extern void tsp_generic_timer_handler(void);
+extern void tsp_generic_timer_stop(void);
+extern void tsp_generic_timer_save(void);
+extern void tsp_generic_timer_restore(void);
#endif /* __ASSEMBLY__ */
#endif /* __BL2_H__ */
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 68bab36..920dfc9 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -265,6 +265,28 @@
((aif) & SPSR_AIF_MASK) << SPSR_AIF_SHIFT)
+/* Physical timer control register bit fields shifts and masks */
+#define CNTP_CTL_ENABLE_SHIFT 0
+#define CNTP_CTL_IMASK_SHIFT 1
+#define CNTP_CTL_ISTATUS_SHIFT 2
+
+#define CNTP_CTL_ENABLE_MASK 1
+#define CNTP_CTL_IMASK_MASK 1
+#define CNTP_CTL_ISTATUS_MASK 1
+
+#define get_cntp_ctl_enable(x) ((x >> CNTP_CTL_ENABLE_SHIFT) & \
+ CNTP_CTL_ENABLE_MASK)
+#define get_cntp_ctl_imask(x) ((x >> CNTP_CTL_IMASK_SHIFT) & \
+ CNTP_CTL_IMASK_MASK)
+#define get_cntp_ctl_istatus(x) ((x >> CNTP_CTL_ISTATUS_SHIFT) & \
+ CNTP_CTL_ISTATUS_MASK)
+
+#define set_cntp_ctl_enable(x) (x |= 1 << CNTP_CTL_ENABLE_SHIFT)
+#define set_cntp_ctl_imask(x) (x |= 1 << CNTP_CTL_IMASK_SHIFT)
+
+#define clr_cntp_ctl_enable(x) (x &= ~(1 << CNTP_CTL_ENABLE_SHIFT))
+#define clr_cntp_ctl_imask(x) (x &= ~(1 << CNTP_CTL_IMASK_SHIFT))
+
/* Miscellaneous MMU related constants */
#define NUM_2MB_IN_GB (1 << 9)
#define NUM_4K_IN_2MB (1 << 9)
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 0a398d0..f30301d 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -202,6 +202,10 @@
extern unsigned long read_cpacr(void);
extern unsigned long read_cpuectlr(void);
extern unsigned int read_cntfrq_el0(void);
+extern unsigned int read_cntps_ctl_el1(void);
+extern unsigned int read_cntps_tval_el1(void);
+extern unsigned long read_cntps_cval_el1(void);
+extern unsigned long read_cntpct_el0(void);
extern unsigned long read_cnthctl_el2(void);
extern unsigned long read_tpidr_el3(void);
@@ -210,6 +214,9 @@
extern void write_hcr(unsigned long);
extern void write_cpacr(unsigned long);
extern void write_cntfrq_el0(unsigned int);
+extern void write_cntps_ctl_el1(unsigned int);
+extern void write_cntps_tval_el1(unsigned int);
+extern void write_cntps_cval_el1(unsigned long);
extern void write_cnthctl_el2(unsigned long);
extern void write_vbar_el1(unsigned long);
diff --git a/lib/aarch64/sysreg_helpers.S b/lib/aarch64/sysreg_helpers.S
index c86fdba..925e93e 100644
--- a/lib/aarch64/sysreg_helpers.S
+++ b/lib/aarch64/sysreg_helpers.S
@@ -142,6 +142,15 @@
.globl read_cntfrq_el0
.globl write_cntfrq_el0
+ .globl read_cntps_ctl_el1
+ .globl write_cntps_ctl_el1
+
+ .globl read_cntps_cval_el1
+ .globl write_cntps_cval_el1
+
+ .globl read_cntps_tval_el1
+ .globl write_cntps_tval_el1
+
.globl read_scr
.globl write_scr
@@ -151,6 +160,7 @@
.globl read_midr
.globl read_mpidr
+ .globl read_cntpct_el0
.globl read_current_el
.globl read_id_pfr1_el1
.globl read_id_aa64pfr0_el1
@@ -672,6 +682,33 @@
msr cntfrq_el0, x0
ret
+func read_cntps_ctl_el1
+ mrs x0, cntps_ctl_el1
+ ret
+
+func write_cntps_ctl_el1
+ msr cntps_ctl_el1, x0
+ ret
+
+func read_cntps_cval_el1
+ mrs x0, cntps_cval_el1
+ ret
+
+func write_cntps_cval_el1
+ msr cntps_cval_el1, x0
+ ret
+
+func read_cntps_tval_el1
+ mrs x0, cntps_tval_el1
+ ret
+
+func write_cntps_tval_el1
+ msr cntps_tval_el1, x0
+ ret
+
+func read_cntpct_el0
+ mrs x0, cntpct_el0
+ ret
func read_cpuectlr
mrs x0, CPUECTLR_EL1
diff --git a/services/spd/tspd/tspd_common.c b/services/spd/tspd/tspd_common.c
index 2d0b08b..d975ec4 100644
--- a/services/spd/tspd/tspd_common.c
+++ b/services/spd/tspd/tspd_common.c
@@ -65,10 +65,14 @@
*/
memset(tsp_ctx, 0, sizeof(*tsp_ctx));
- /* Set the right security state and register width for the SP */
+ /*
+ * Set the right security state, register width and enable access to
+ * the secure physical timer for the SP.
+ */
scr = read_scr();
scr &= ~SCR_NS_BIT;
scr &= ~SCR_RW_BIT;
+ scr |= SCR_ST_BIT;
if (rw == TSP_AARCH64)
scr |= SCR_RW_BIT;