diff options
author | Ajay Nandakumar <anandakumarm@nvidia.com> | 2013-09-26 19:18:41 +0530 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2013-10-17 03:51:45 -0700 |
commit | 92cdd8ac7f78feee3995cee31a6c8368ebba211d (patch) | |
tree | 373407eb1920ff6ae129cf44d57a88c6d278001a /drivers/clocksource | |
parent | 7385bf933876ae3502e56e66c7df1f1fdbdbe4b7 (diff) |
ARM: tegra: move tsc timers to drivers/clocksource
Moving tegra tsc timer to drivers/clocksource
Bug 1379817
Change-Id: Ifa1b1b1638960c390f3fca4ba2261a5692cc8f61
Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com>
Reviewed-on: http://git-master/r/299021
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/tegra-tsc-timer.c | 224 |
2 files changed, 225 insertions, 0 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ef212f8dc882..ee5306157091 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_CLK_SRC_TEGRA_TIMER) += tegra-nvtimers.o obj-$(CONFIG_CLK_SRC_TEGRA_TIMER) += tegra-wakeup-nvtimers.o +obj-$(CONFIG_ARM_ARCH_TIMER) += tegra-tsc-timer.o diff --git a/drivers/clocksource/tegra-tsc-timer.c b/drivers/clocksource/tegra-tsc-timer.c new file mode 100644 index 000000000000..77a2d3f8bdaf --- /dev/null +++ b/drivers/clocksource/tegra-tsc-timer.c @@ -0,0 +1,224 @@ +/* + * drivers/clocksource/tegra-tsc-timer.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@google.com> + * + * Copyright (C) 2013 NVIDIA CORPORATION. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/init.h> +#include <linux/err.h> +#include <linux/time.h> +#include <linux/clocksource.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/cpu_pm.h> +#include <linux/of.h> +#include <linux/tegra-timer.h> + +#include <asm/mach/time.h> +#include <asm/cputype.h> +#include <asm/system.h> +#include <asm/arch_timer.h> +#include <asm/sched_clock.h> +#include <asm/delay.h> + +#include "../../arch/arm/mach-tegra/clock.h" + + +static u32 arch_timer_us_mult, arch_timer_us_shift; + +bool arch_timer_initialized; +static struct delay_timer arch_delay_timer; + +#ifdef CONFIG_TEGRA_PRE_SILICON_SUPPORT +#ifndef CONFIG_TRUSTED_FOUNDATIONS +/* Time Stamp Counter (TSC) base address */ +static void __iomem *tsc = IO_ADDRESS(TEGRA_TSC_BASE); +#endif + +#define TSC_CNTCR 0 /* TSC control registers */ +#define TSC_CNTCR_ENABLE (1 << 0) /* Enable*/ +#define TSC_CNTCR_HDBG (1 << 1) /* Halt on debug */ + +#define TSC_CNTCV0 0x8 /* TSC counter (LSW) */ +#define TSC_CNTCV1 0xC /* TSC counter (MSW) */ +#define TSC_CNTFID0 0x20 /* TSC freq id 0 */ + +#define tsc_writel(value, reg) \ + __raw_writel(value, (u32)tsc + (reg)) +#define tsc_readl(reg) \ + __raw_readl((u32)tsc + (reg)) +#endif /* CONFIG_TEGRA_PRE_SILICON_SUPPORT */ + +/* Is the optional system timer available? */ +static int local_timer_is_architected(void) +{ +#ifdef CONFIG_ARM64 + return 1; +#else + return (cpu_architecture() >= CPU_ARCH_ARMv7) && + ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1; +#endif +} + +static unsigned long arch_timer_read_current_timer(void) +{ + cycle_t cval = 0; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); + return cval; +} + +void __init tegra_cpu_timer_init(void) +{ + u32 tsc_ref_freq; +#ifdef CONFIG_TEGRA_PRE_SILICON_SUPPORT + u32 reg; +#endif + + if (!local_timer_is_architected()) + return; + + tsc_ref_freq = tegra_clk_measure_input_freq(); + if (tsc_ref_freq == 115200 || tsc_ref_freq == 230400) { + /* + * OSC detection function will bug out if revision is not + * QT and the detected frequency is one of these two. + */ + tsc_ref_freq = 13000000; + pr_info("fake tsc_ref_req=%d in QT\n", tsc_ref_freq); + } + +#ifdef CONFIG_TEGRA_PRE_SILICON_SUPPORT + if (tegra_platform_is_linsim()) { + /* Set the Timer System Counter (TSC) reference frequency + NOTE: this is a write once register */ + tsc_writel(tsc_ref_freq, TSC_CNTFID0); + + /* Program CNTFRQ to the same value. + NOTE: this is a write once (per CPU reset) register. */ + __asm__("mcr p15, 0, %0, c14, c0, 0\n" : : "r" (tsc_ref_freq)); + + /* CNTFRQ must agree with the TSC reference frequency. */ + __asm__("mrc p15, 0, %0, c14, c0, 0\n" : "=r" (reg)); + BUG_ON(reg != tsc_ref_freq); + + /* Enable the TSC. */ + reg = tsc_readl(TSC_CNTCR); + reg |= TSC_CNTCR_ENABLE | TSC_CNTCR_HDBG; + tsc_writel(reg, TSC_CNTCR); + } +#endif + clocks_calc_mult_shift(&arch_timer_us_mult, &arch_timer_us_shift, + tsc_ref_freq, USEC_PER_SEC, 0); + + /* register arch timer as delay timer */ + arch_delay_timer.read_current_timer = &arch_timer_read_current_timer; + arch_delay_timer.freq = tsc_ref_freq; + register_current_timer_delay(&arch_delay_timer); + + return; +} + +static void tegra_arch_timer_per_cpu_init(void) +{ +#if defined(CONFIG_TEGRA_USE_SECURE_KERNEL) + return; +#else + if (arch_timer_initialized) { + u32 tsc_ref_freq = tegra_clk_measure_input_freq(); + + /* + * OSC detection function will bug out if revision is not QT and + * the detected frequency is one of these two. + */ + if (tsc_ref_freq == 115200 || tsc_ref_freq == 230400) + tsc_ref_freq = 13000000; + + /* Program CNTFRQ to the input frequency. + NOTE: this is a write once (per CPU reset) register. */ + __asm__("mcr p15, 0, %0, c14, c0, 0\n" : : "r" (tsc_ref_freq)); + } +#endif +} + +static int arch_timer_cpu_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + switch (action) { + case CPU_STARTING: + case CPU_STARTING_FROZEN: + tegra_arch_timer_per_cpu_init(); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block arch_timer_cpu_nb = { + .notifier_call = arch_timer_cpu_notify, +}; + +static int arch_timer_cpu_pm_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + switch (action) { + case CPU_PM_EXIT: + tegra_arch_timer_per_cpu_init(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block arch_timer_cpu_pm_nb = { + .notifier_call = arch_timer_cpu_pm_notify, +}; + +int __init tegra_init_arch_timer(void) +{ + if (!local_timer_is_architected()) + return -ENODEV; + + clocksource_of_init(); + + register_cpu_notifier(&arch_timer_cpu_nb); + cpu_pm_register_notifier(&arch_timer_cpu_pm_nb); + arch_timer_initialized = true; + return 0; +} + +void __init tegra_init_late_timer(void) +{} + +int tegra_cpu_timer_get_remain(s64 *time) +{ + s32 cntp_tval; + int ret = 0; + + asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (cntp_tval)); + + if (cntp_tval <= 0) + ret = -ETIME; + else + *time = (s64)((s64)cntp_tval * arch_timer_us_mult) + >> arch_timer_us_shift; + + return ret; +} |