summaryrefslogtreecommitdiff
path: root/drivers/clocksource/tegra-tsc-timer.c
blob: 10f4dbce9d1f7cce2fe5190a39426cc0844def4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * drivers/clocksource/tegra-tsc-timer.c
 *
 * Copyright (C) 2010 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@google.com>
 *
 * Copyright (C) 2013-2014 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 <linux/tegra-soc.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>

#include <asm/mach/time.h>
#include <asm/cputype.h>
#include <asm/system.h>
#include <asm/arch_timer.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;
#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, tsc + (reg))
#define tsc_readl(reg) \
	__raw_readl(tsc + (reg))
#endif /* CONFIG_TEGRA_PRE_SILICON_SUPPORT */

/* Is the optional system timer available? */
static int local_timer_is_architected(void)
{
	return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
	       ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
}

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 (!is_secure_mode())
		return;

	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));
	}
}

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;

	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;
}


#if defined(CONFIG_TEGRA_PRE_SILICON_SUPPORT) && \
			!defined(CONFIG_TRUSTED_FOUNDATIONS)
static void __init tegra_init_tsc(struct device_node *np)
{
	tsc = of_iomap(np, 0);
	if (!tsc) {
		pr_err("%s: Can't map tsc registers", __func__);
		BUG();
	}
}
CLOCKSOURCE_OF_DECLARE(tegra_tsc, "nvidia,tegra-tsc", tegra_init_tsc);
#endif