aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/tegra20_timer.c
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2013-02-05 15:13:10 -0500
committerOlof Johansson <olof@lixom.net>2013-02-05 15:13:10 -0500
commitbda6f8e6cdcdb55db9b2961b6a7c9d0d97da4765 (patch)
treecec7c71c5120538352157fa1d2826efe881a7b0c /drivers/clocksource/tegra20_timer.c
parentc35a0bfacb61f5c56e0e64f309d36c59c7fe8da3 (diff)
parentef3ffe5a0458606c488def757bb7f6dd013c2db5 (diff)
Merge tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc
From Stephen Warren: ARM: tegra: Common Clock Framework rework Tegra already supports the common clock framework, but had issues: 1) The clock driver was located in arch/arm/mach-tegra/ rather than drivers/clk/. 2) A single "Tegra clock" type was implemented, rather than separate clock types for PLL, mux, divider, ... type in HW. 3) Clock lookups by device drivers were still driven by device name and connection ID, rather than through device tree. This pull request solves all three issues. This required some DT changes to add clocks properties, and driver changes to request clocks more "correctly". Finally, this rework allows all AUXDATA to be removed from Tegra board files, and various duplicate clock lookup entries to be removed from the driver. This pull request is based on the previous pull request, with tag tegra-for-3.9-cleanup. * tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: (31 commits) clk: tegra30: remove unused TEGRA_CLK_DUPLICATE()s clk: tegra20: remove unused TEGRA_CLK_DUPLICATE()s ARM: tegra30: remove auxdata ARM: tegra20: remove auxdata ASoC: tegra: remove auxdata staging: nvec: remove use of clk_get_sys ARM: tegra: paz00: add clock information to DT ARM: tegra: add clock properties to Tegra30 DT ARM: tegra: add clock properties to Tegra20 DT spi: tegra: do not use clock name to get clock ARM: tegra: remove legacy clock code ARM: tegra: migrate to new clock code clk: tegra: add clock support for Tegra30 clk: tegra: add clock support for Tegra20 clk: tegra: add Tegra specific clocks ARM: tegra: define Tegra30 CAR binding ARM: tegra: define Tegra20 CAR binding ARM: tegra: move tegra_cpu_car.h to linux/clk/tegra.h ARM: tegra: add function to read chipid ARM: tegra: fix compile error when disable CPU_IDLE ... Signed-off-by: Olof Johansson <olof@lixom.net> Conflicts: arch/arm/mach-tegra/board-dt-tegra20.c arch/arm/mach-tegra/board-dt-tegra30.c arch/arm/mach-tegra/common.c arch/arm/mach-tegra/platsmp.c drivers/clocksource/Makefile
Diffstat (limited to 'drivers/clocksource/tegra20_timer.c')
-rw-r--r--drivers/clocksource/tegra20_timer.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
new file mode 100644
index 000000000000..0bde03feb095
--- /dev/null
+++ b/drivers/clocksource/tegra20_timer.c
@@ -0,0 +1,281 @@
1/*
2 * Copyright (C) 2010 Google, Inc.
3 *
4 * Author:
5 * Colin Cross <ccross@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/init.h>
19#include <linux/err.h>
20#include <linux/time.h>
21#include <linux/interrupt.h>
22#include <linux/irq.h>
23#include <linux/clockchips.h>
24#include <linux/clocksource.h>
25#include <linux/clk.h>
26#include <linux/io.h>
27#include <linux/of_address.h>
28#include <linux/of_irq.h>
29
30#include <asm/mach/time.h>
31#include <asm/smp_twd.h>
32#include <asm/sched_clock.h>
33
34#define RTC_SECONDS 0x08
35#define RTC_SHADOW_SECONDS 0x0c
36#define RTC_MILLISECONDS 0x10
37
38#define TIMERUS_CNTR_1US 0x10
39#define TIMERUS_USEC_CFG 0x14
40#define TIMERUS_CNTR_FREEZE 0x4c
41
42#define TIMER1_BASE 0x0
43#define TIMER2_BASE 0x8
44#define TIMER3_BASE 0x50
45#define TIMER4_BASE 0x58
46
47#define TIMER_PTV 0x0
48#define TIMER_PCR 0x4
49
50static void __iomem *timer_reg_base;
51static void __iomem *rtc_base;
52
53static struct timespec persistent_ts;
54static u64 persistent_ms, last_persistent_ms;
55
56#define timer_writel(value, reg) \
57 __raw_writel(value, timer_reg_base + (reg))
58#define timer_readl(reg) \
59 __raw_readl(timer_reg_base + (reg))
60
61static int tegra_timer_set_next_event(unsigned long cycles,
62 struct clock_event_device *evt)
63{
64 u32 reg;
65
66 reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0);
67 timer_writel(reg, TIMER3_BASE + TIMER_PTV);
68
69 return 0;
70}
71
72static void tegra_timer_set_mode(enum clock_event_mode mode,
73 struct clock_event_device *evt)
74{
75 u32 reg;
76
77 timer_writel(0, TIMER3_BASE + TIMER_PTV);
78
79 switch (mode) {
80 case CLOCK_EVT_MODE_PERIODIC:
81 reg = 0xC0000000 | ((1000000/HZ)-1);
82 timer_writel(reg, TIMER3_BASE + TIMER_PTV);
83 break;
84 case CLOCK_EVT_MODE_ONESHOT:
85 break;
86 case CLOCK_EVT_MODE_UNUSED:
87 case CLOCK_EVT_MODE_SHUTDOWN:
88 case CLOCK_EVT_MODE_RESUME:
89 break;
90 }
91}
92
93static struct clock_event_device tegra_clockevent = {
94 .name = "timer0",
95 .rating = 300,
96 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
97 .set_next_event = tegra_timer_set_next_event,
98 .set_mode = tegra_timer_set_mode,
99};
100
101static u32 notrace tegra_read_sched_clock(void)
102{
103 return timer_readl(TIMERUS_CNTR_1US);
104}
105
106/*
107 * tegra_rtc_read - Reads the Tegra RTC registers
108 * Care must be taken that this funciton is not called while the
109 * tegra_rtc driver could be executing to avoid race conditions
110 * on the RTC shadow register
111 */
112static u64 tegra_rtc_read_ms(void)
113{
114 u32 ms = readl(rtc_base + RTC_MILLISECONDS);
115 u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
116 return (u64)s * MSEC_PER_SEC + ms;
117}
118
119/*
120 * tegra_read_persistent_clock - Return time from a persistent clock.
121 *
122 * Reads the time from a source which isn't disabled during PM, the
123 * 32k sync timer. Convert the cycles elapsed since last read into
124 * nsecs and adds to a monotonically increasing timespec.
125 * Care must be taken that this funciton is not called while the
126 * tegra_rtc driver could be executing to avoid race conditions
127 * on the RTC shadow register
128 */
129static void tegra_read_persistent_clock(struct timespec *ts)
130{
131 u64 delta;
132 struct timespec *tsp = &persistent_ts;
133
134 last_persistent_ms = persistent_ms;
135 persistent_ms = tegra_rtc_read_ms();
136 delta = persistent_ms - last_persistent_ms;
137
138 timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
139 *ts = *tsp;
140}
141
142static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
143{
144 struct clock_event_device *evt = (struct clock_event_device *)dev_id;
145 timer_writel(1<<30, TIMER3_BASE + TIMER_PCR);
146 evt->event_handler(evt);
147 return IRQ_HANDLED;
148}
149
150static struct irqaction tegra_timer_irq = {
151 .name = "timer0",
152 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH,
153 .handler = tegra_timer_interrupt,
154 .dev_id = &tegra_clockevent,
155};
156
157static const struct of_device_id timer_match[] __initconst = {
158 { .compatible = "nvidia,tegra20-timer" },
159 {}
160};
161
162static const struct of_device_id rtc_match[] __initconst = {
163 { .compatible = "nvidia,tegra20-rtc" },
164 {}
165};
166
167static void __init tegra20_init_timer(void)
168{
169 struct device_node *np;
170 struct clk *clk;
171 unsigned long rate;
172 int ret;
173
174 np = of_find_matching_node(NULL, timer_match);
175 if (!np) {
176 pr_err("Failed to find timer DT node\n");
177 BUG();
178 }
179
180 timer_reg_base = of_iomap(np, 0);
181 if (!timer_reg_base) {
182 pr_err("Can't map timer registers\n");
183 BUG();
184 }
185
186 tegra_timer_irq.irq = irq_of_parse_and_map(np, 2);
187 if (tegra_timer_irq.irq <= 0) {
188 pr_err("Failed to map timer IRQ\n");
189 BUG();
190 }
191
192 clk = clk_get_sys("timer", NULL);
193 if (IS_ERR(clk)) {
194 pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");
195 rate = 12000000;
196 } else {
197 clk_prepare_enable(clk);
198 rate = clk_get_rate(clk);
199 }
200
201 of_node_put(np);
202
203 np = of_find_matching_node(NULL, rtc_match);
204 if (!np) {
205 pr_err("Failed to find RTC DT node\n");
206 BUG();
207 }
208
209 rtc_base = of_iomap(np, 0);
210 if (!rtc_base) {
211 pr_err("Can't map RTC registers");
212 BUG();
213 }
214
215 /*
216 * rtc registers are used by read_persistent_clock, keep the rtc clock
217 * enabled
218 */
219 clk = clk_get_sys("rtc-tegra", NULL);
220 if (IS_ERR(clk))
221 pr_warn("Unable to get rtc-tegra clock\n");
222 else
223 clk_prepare_enable(clk);
224
225 of_node_put(np);
226
227 switch (rate) {
228 case 12000000:
229 timer_writel(0x000b, TIMERUS_USEC_CFG);
230 break;
231 case 13000000:
232 timer_writel(0x000c, TIMERUS_USEC_CFG);
233 break;
234 case 19200000:
235 timer_writel(0x045f, TIMERUS_USEC_CFG);
236 break;
237 case 26000000:
238 timer_writel(0x0019, TIMERUS_USEC_CFG);
239 break;
240 default:
241 WARN(1, "Unknown clock rate");
242 }
243
244 setup_sched_clock(tegra_read_sched_clock, 32, 1000000);
245
246 if (clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US,
247 "timer_us", 1000000, 300, 32, clocksource_mmio_readl_up)) {
248 pr_err("Failed to register clocksource\n");
249 BUG();
250 }
251
252 ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq);
253 if (ret) {
254 pr_err("Failed to register timer IRQ: %d\n", ret);
255 BUG();
256 }
257
258 tegra_clockevent.cpumask = cpu_all_mask;
259 tegra_clockevent.irq = tegra_timer_irq.irq;
260 clockevents_config_and_register(&tegra_clockevent, 1000000,
261 0x1, 0x1fffffff);
262#ifdef CONFIG_HAVE_ARM_TWD
263 twd_local_timer_of_register();
264#endif
265 register_persistent_clock(NULL, tegra_read_persistent_clock);
266}
267CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer);
268
269#ifdef CONFIG_PM
270static u32 usec_config;
271
272void tegra_timer_suspend(void)
273{
274 usec_config = timer_readl(TIMERUS_USEC_CFG);
275}
276
277void tegra_timer_resume(void)
278{
279 timer_writel(usec_config, TIMERUS_USEC_CFG);
280}
281#endif