aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/timer-t3.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /arch/arm/mach-tegra/timer-t3.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'arch/arm/mach-tegra/timer-t3.c')
-rw-r--r--arch/arm/mach-tegra/timer-t3.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/timer-t3.c b/arch/arm/mach-tegra/timer-t3.c
new file mode 100644
index 00000000000..df964fbce15
--- /dev/null
+++ b/arch/arm/mach-tegra/timer-t3.c
@@ -0,0 +1,338 @@
1/*
2 * arch/arch/mach-tegra/timer-t3.c
3 *
4 * Copyright (c) 2011-2012, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/init.h>
22#include <linux/err.h>
23#include <linux/sched.h>
24#include <linux/time.h>
25#include <linux/interrupt.h>
26#include <linux/irq.h>
27#include <linux/clockchips.h>
28#include <linux/clocksource.h>
29#include <linux/clk.h>
30#include <linux/io.h>
31#include <linux/smp.h>
32#include <linux/syscore_ops.h>
33#include <linux/cpu.h>
34
35#include <asm/mach/time.h>
36#include <asm/localtimer.h>
37#include <asm/sched_clock.h>
38
39#include <mach/hardware.h>
40#include <mach/iomap.h>
41#include <mach/irqs.h>
42
43#include "board.h"
44#include "clock.h"
45#include "cpuidle.h"
46#include "timer.h"
47
48#define TEST_LP2_WAKE_TIMERS 0
49
50/*
51 * Timers usage:
52 * TMR1 - used as general CPU timer.
53 * TMR2 - used by AVP.
54 * TMR3 - used by CPU0 for LP2 wakeup.
55 * TMR4 - used by CPU1 for LP2 wakeup.
56 * TMR5 - used by CPU2 for LP2 wakeup.
57 * TMR6 - used by CPU3 for LP2 wakeup.
58 * TMR7 - Free.
59 * TMR8 - Free.
60 * TMR9 - Free.
61 * TMR10 - used as source for watchdog controller 0.
62*/
63
64#define TIMER1_OFFSET (TEGRA_TMR1_BASE-TEGRA_TMR1_BASE)
65#define TIMER2_OFFSET (TEGRA_TMR2_BASE-TEGRA_TMR1_BASE)
66#define TIMER3_OFFSET (TEGRA_TMR3_BASE-TEGRA_TMR1_BASE)
67#define TIMER4_OFFSET (TEGRA_TMR4_BASE-TEGRA_TMR1_BASE)
68#define TIMER5_OFFSET (TEGRA_TMR5_BASE-TEGRA_TMR1_BASE)
69#define TIMER6_OFFSET (TEGRA_TMR6_BASE-TEGRA_TMR1_BASE)
70
71static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
72static cpumask_t wake_timer_ready;
73static cpumask_t wake_timer_canceled;
74
75#define timer_writel(value, reg) \
76 __raw_writel(value, (u32)timer_reg_base + (reg))
77#define timer_readl(reg) \
78 __raw_readl((u32)timer_reg_base + (reg))
79
80
81#ifdef CONFIG_PM_SLEEP
82static u32 lp2_wake_timers[] = {
83 TIMER3_OFFSET,
84#ifdef CONFIG_SMP
85 TIMER4_OFFSET,
86 TIMER5_OFFSET,
87 TIMER6_OFFSET,
88#endif
89};
90
91static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
92{
93 int cpu = (int)dev_id;
94 int base;
95
96 base = lp2_wake_timers[cpu];
97 timer_writel(1<<30, base + TIMER_PCR);
98 return IRQ_HANDLED;
99}
100
101#define LP2_TIMER_IRQ_ACTION(cpu, irqnum) { \
102 .name = "tmr_lp2wake_cpu" __stringify(cpu), \
103 .flags = IRQF_DISABLED, \
104 .handler = tegra_lp2wake_interrupt, \
105 .dev_id = (void*)cpu, \
106 .irq = irqnum }
107
108static struct irqaction tegra_lp2wake_irq[] = {
109 LP2_TIMER_IRQ_ACTION(0, INT_TMR3),
110#ifdef CONFIG_SMP
111 LP2_TIMER_IRQ_ACTION(1, INT_TMR4),
112 LP2_TIMER_IRQ_ACTION(2, INT_TMR5),
113 LP2_TIMER_IRQ_ACTION(3, INT_TMR6),
114#endif
115};
116
117#ifdef CONFIG_SMP
118#define hard_smp_processor_id() \
119 ({ \
120 unsigned int cpunum; \
121 __asm__("\n" \
122 "1: mrc p15, 0, %0, c0, c0, 5\n" \
123 " .pushsection \".alt.smp.init\", \"a\"\n"\
124 " .long 1b\n" \
125 " mov %0, #0\n" \
126 " .popsection" \
127 : "=r" (cpunum)); \
128 cpunum &= 0x0F; \
129 })
130#define cpu_number() hard_smp_processor_id()
131#else
132#define cpu_number() 0
133#endif
134
135/*
136 * To sanity test LP2 timer interrupts for CPU 0-3, enable this flag and check
137 * /proc/interrupts for timer interrupts. CPUs 0-3 should have one interrupt
138 * counted against them for tmr_lp2wake_cpu<n>, where <n> is the CPU number.
139 */
140#if TEST_LP2_WAKE_TIMERS
141static void test_lp2_wake_timer(unsigned int cpu)
142{
143 unsigned long cycles = 50000;
144 unsigned int base = lp2_wake_timers[cpu];
145 static bool tested[4] = {false, false, false, false};
146
147 /* Don't repeat the test process on hotplug restart. */
148 if (!tested[cpu]) {
149 timer_writel(0, base + TIMER_PTV);
150 if (cycles) {
151 u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
152 timer_writel(reg, base + TIMER_PTV);
153 tested[cpu] = true;
154 }
155 }
156}
157#else
158static inline void test_lp2_wake_timer(unsigned int cpu) {}
159#endif
160
161static int tegra3_resume_wake_timer(unsigned int cpu)
162{
163#ifdef CONFIG_SMP
164 int ret = irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(cpu));
165 if (ret) {
166 pr_err("Failed to set affinity for LP2 timer IRQ to "
167 "CPU %d: irq=%d, ret=%d\n", cpu,
168 tegra_lp2wake_irq[cpu].irq, ret);
169 return ret;
170 }
171#endif
172 cpumask_set_cpu(cpu, &wake_timer_ready);
173 return 0;
174}
175
176static void tegra3_register_wake_timer(unsigned int cpu)
177{
178 int ret;
179
180 ret = setup_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
181 if (ret) {
182 pr_err("Failed to register LP2 timer IRQ for CPU %d: "
183 "irq=%d, ret=%d\n", cpu,
184 tegra_lp2wake_irq[cpu].irq, ret);
185 goto fail;
186 }
187
188 ret = tegra3_resume_wake_timer(cpu);
189 if (ret)
190 goto fail;
191
192 test_lp2_wake_timer(cpu);
193 return;
194fail:
195 tegra_lp2_in_idle(false);
196}
197
198#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
199static void tegra3_suspend_wake_timer(unsigned int cpu)
200{
201 cpumask_clear_cpu(cpu, &wake_timer_ready);
202#ifdef CONFIG_SMP
203 /* Reassign the affinity of the wake IRQ to CPU 0. */
204 (void)irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(0));
205#endif
206}
207
208static void tegra3_unregister_wake_timer(unsigned int cpu)
209{
210 tegra3_suspend_wake_timer(cpu);
211
212 /* Dispose of this IRQ. */
213 remove_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
214}
215#endif
216
217void tegra3_lp2_set_trigger(unsigned long cycles)
218{
219 int cpu = cpu_number();
220 int base;
221
222 base = lp2_wake_timers[cpu];
223 timer_writel(0, base + TIMER_PTV);
224 if (cycles) {
225 u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
226 timer_writel(reg, base + TIMER_PTV);
227 }
228}
229EXPORT_SYMBOL(tegra3_lp2_set_trigger);
230
231unsigned long tegra3_lp2_timer_remain(void)
232{
233 int cpu = cpu_number();
234
235 if (cpumask_test_and_clear_cpu(cpu, &wake_timer_canceled))
236 return -ETIME;
237
238 return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful;
239}
240
241int tegra3_is_lp2_timer_ready(unsigned int cpu)
242{
243 return cpumask_test_cpu(cpu, &wake_timer_ready);
244}
245
246void tegra3_lp2_timer_cancel_secondary(void)
247{
248 int cpu;
249 int base;
250
251 for (cpu = 1; cpu < ARRAY_SIZE(lp2_wake_timers); cpu++) {
252 base = lp2_wake_timers[cpu];
253 cpumask_set_cpu(cpu, &wake_timer_canceled);
254 timer_writel(0, base + TIMER_PTV);
255 timer_writel(1<<30, base + TIMER_PCR);
256 }
257}
258#endif
259
260void __init tegra3_init_timer(u32 *offset, int *irq)
261{
262 unsigned long rate = tegra_clk_measure_input_freq();
263
264 switch (rate) {
265 case 12000000:
266 timer_writel(0x000b, TIMERUS_USEC_CFG);
267 break;
268 case 13000000:
269 timer_writel(0x000c, TIMERUS_USEC_CFG);
270 break;
271 case 19200000:
272 timer_writel(0x045f, TIMERUS_USEC_CFG);
273 break;
274 case 26000000:
275 timer_writel(0x0019, TIMERUS_USEC_CFG);
276 break;
277 case 16800000:
278 timer_writel(0x0453, TIMERUS_USEC_CFG);
279 break;
280 case 38400000:
281 timer_writel(0x04BF, TIMERUS_USEC_CFG);
282 break;
283 case 48000000:
284 timer_writel(0x002F, TIMERUS_USEC_CFG);
285 break;
286 default:
287 WARN(1, "Unknown clock rate");
288 }
289
290#ifdef CONFIG_PM_SLEEP
291#ifdef CONFIG_SMP
292 /* For T30.A01 use INT_TMR_SHARED instead of INT_TMR6 for CPU3. */
293 if ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) &&
294 (tegra_get_revision() == TEGRA_REVISION_A01))
295 tegra_lp2wake_irq[3].irq = INT_TMR_SHARED;
296#endif
297
298 tegra3_register_wake_timer(0);
299#endif
300
301 *offset = TIMER1_OFFSET;
302 *irq = INT_TMR1;
303}
304
305#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
306static int hotplug_notify(struct notifier_block *self,
307 unsigned long action, void *cpu)
308{
309 switch (action) {
310 case CPU_ONLINE:
311 tegra3_register_wake_timer((unsigned int)cpu);
312 break;
313 case CPU_ONLINE_FROZEN:
314 tegra3_resume_wake_timer((unsigned int)cpu);
315 break;
316 case CPU_DOWN_PREPARE:
317 tegra3_unregister_wake_timer((unsigned int)cpu);
318 break;
319 case CPU_DOWN_PREPARE_FROZEN:
320 tegra3_suspend_wake_timer((unsigned int)cpu);
321 break;
322 default:
323 break;
324 }
325
326 return NOTIFY_OK;
327}
328
329static struct notifier_block __cpuinitdata hotplug_notifier_block = {
330 .notifier_call = hotplug_notify,
331};
332
333static int __init hotplug_cpu_register(void)
334{
335 return register_cpu_notifier(&hotplug_notifier_block);
336}
337early_initcall(hotplug_cpu_register);
338#endif