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;