Tegra: delay_timer: support for physical secure timer

This patch modifies the delay timer driver to switch to the ARM
secure physical timer instead of using Tegra's on-chip uS timer.

The secure timer is not accessible to the NS world and so eliminates
an important attack vector, where the Tegra timer source gets switched
off from the NS world leading to a DoS attack for the trusted world.

This timer is shared with the S-EL1 layer for now, but later patches
will mark it as exclusive to the EL3 exception mode.

Change-Id: I2c00f8cb4c48b25578971c626c314603906ad7cc
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/plat/nvidia/tegra/common/tegra_delay_timer.c b/plat/nvidia/tegra/common/tegra_delay_timer.c
index 63dcf41..cfd9a15 100644
--- a/plat/nvidia/tegra/common/tegra_delay_timer.c
+++ b/plat/nvidia/tegra/common/tegra_delay_timer.c
@@ -1,31 +1,59 @@
 /*
  * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <arch.h>
+
 #include <drivers/delay_timer.h>
 #include <lib/mmio.h>
+#include <lib/utils_def.h>
+#include <plat/common/platform.h>
 
 #include <tegra_def.h>
 #include <tegra_private.h>
 
-static uint32_t tegra_timerus_get_value(void)
+static uint32_t tegra_timer_get_value(void)
 {
-	return mmio_read_32(TEGRA_TMRUS_BASE);
+	/* enable cntps_tval_el1 timer, mask interrupt */
+	write_cntps_ctl_el1(CNTP_CTL_IMASK_BIT | CNTP_CTL_ENABLE_BIT);
+
+	/*
+	 * Generic delay timer implementation expects the timer to be a down
+	 * counter. We apply bitwise NOT operator to the tick values returned
+	 * by read_cntps_tval_el1() to simulate the down counter. The value is
+	 * clipped from 64 to 32 bits.
+	 */
+	return (uint32_t)(~read_cntps_tval_el1());
 }
 
 /*
- * Initialise the on-chip free rolling us counter as the delay
- * timer.
+ * Initialise the architecture provided counter as the delay timer.
  */
 void tegra_delay_timer_init(void)
 {
-	static const timer_ops_t tegra_timer_ops = {
-		.get_timer_value	= tegra_timerus_get_value,
-		.clk_mult		= 1,
-		.clk_div		= 1,
-	};
+	static timer_ops_t tegra_timer_ops;
+
+	/* Value in ticks */
+	uint32_t multiplier = MHZ_TICKS_PER_SEC;
+
+	/* Value in ticks per second (Hz) */
+	uint32_t divider  = plat_get_syscnt_freq2();
+
+	/* Reduce multiplier and divider by dividing them repeatedly by 10 */
+	while (((multiplier % 10U) == 0U) && ((divider % 10U) == 0U)) {
+		multiplier /= 10U;
+		divider /= 10U;
+	}
+
+	/* enable cntps_tval_el1 timer, mask interrupt */
+	write_cntps_ctl_el1(CNTP_CTL_IMASK_BIT | CNTP_CTL_ENABLE_BIT);
 
+	/* register the timer */
+	tegra_timer_ops.get_timer_value = tegra_timer_get_value;
+	tegra_timer_ops.clk_mult = multiplier;
+	tegra_timer_ops.clk_div = divider;
 	timer_init(&tegra_timer_ops);
 }