]> nv-tegra.nvidia Code Review - linux-3.10.git/blob - arch/arm/mach-tegra/cpu_timer_twd.c
ARM: tegra: dvfs: Add predict peak voltage interface
[linux-3.10.git] / arch / arm / mach-tegra / cpu_timer_twd.c
1 /*
2  * arch/arch/mach-tegra/cpu_timer_twd.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  *
6  * Author:
7  *      Colin Cross <ccross@google.com>
8  *
9  * Copyright (C) 2013 NVIDIA Corporation
10  *
11  * This software is licensed under the terms of the GNU General Public
12  * License version 2, as published by the Free Software Foundation, and
13  * may be copied, distributed, and modified under those terms.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  */
21
22 #include <linux/init.h>
23 #include <linux/err.h>
24 #include <linux/time.h>
25 #include <linux/io.h>
26 #include <linux/of.h>
27
28 #include <asm/mach/time.h>
29 #include <asm/cputype.h>
30 #include <asm/system.h>
31 #include <asm/smp_twd.h>
32
33 #include <mach/irqs.h>
34
35 #include "clock.h"
36 #include "iomap.h"
37 #include "timer.h"
38
39 static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
40                               TEGRA_ARM_PERIF_BASE + 0x600,
41                               IRQ_LOCALTIMER);
42 static void __iomem *tegra_twd_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x600);
43
44 void __init tegra_cpu_timer_init(void)
45 {
46         struct clk *cpu, *twd_clk;
47         int err;
48
49         /* The twd clock is a detached child of the CPU complex clock.
50            Force an update of the twd clock after DVFS has updated the
51            CPU clock rate. */
52
53         twd_clk = tegra_get_clock_by_name("twd");
54         BUG_ON(!twd_clk);
55         cpu = tegra_get_clock_by_name("cpu");
56         err = clk_set_rate(twd_clk, clk_get_rate(cpu));
57
58         if (err)
59                 pr_err("Failed to set twd clock rate: %d\n", err);
60         else
61                 pr_debug("TWD clock rate: %ld\n", clk_get_rate(twd_clk));
62 }
63
64 int tegra_twd_get_state(struct tegra_twd_context *context)
65 {
66         context->twd_ctrl = readl(tegra_twd_base + TWD_TIMER_CONTROL);
67         context->twd_load = readl(tegra_twd_base + TWD_TIMER_LOAD);
68         context->twd_cnt = readl(tegra_twd_base + TWD_TIMER_COUNTER);
69
70         return 0;
71 }
72
73 void tegra_twd_suspend(struct tegra_twd_context *context)
74 {
75         context->twd_ctrl = readl(tegra_twd_base + TWD_TIMER_CONTROL);
76         context->twd_load = readl(tegra_twd_base + TWD_TIMER_LOAD);
77         if ((context->twd_load == 0) &&
78             (context->twd_ctrl & TWD_TIMER_CONTROL_PERIODIC) &&
79             (context->twd_ctrl & (TWD_TIMER_CONTROL_ENABLE |
80                                   TWD_TIMER_CONTROL_IT_ENABLE))) {
81                 WARN("%s: TWD enabled but counter was 0\n", __func__);
82                 context->twd_load = 1;
83         }
84         __raw_writel(0, tegra_twd_base + TWD_TIMER_CONTROL);
85 }
86
87 void tegra_twd_resume(struct tegra_twd_context *context)
88 {
89         BUG_ON((context->twd_load == 0) &&
90                (context->twd_ctrl & TWD_TIMER_CONTROL_PERIODIC) &&
91                (context->twd_ctrl & (TWD_TIMER_CONTROL_ENABLE |
92                                      TWD_TIMER_CONTROL_IT_ENABLE)));
93         writel(context->twd_load, tegra_twd_base + TWD_TIMER_LOAD);
94         writel(context->twd_ctrl, tegra_twd_base + TWD_TIMER_CONTROL);
95 }
96
97 void __init tegra_init_late_timer(void)
98 {
99         int err;
100
101         if (of_have_populated_dt()) {
102                 twd_local_timer_of_register();
103                 return;
104         }
105
106         err = twd_local_timer_register(&twd_local_timer);
107         if (err)
108                 pr_err("twd_timer_register failed %d\n", err);
109 }