diff options
Diffstat (limited to 'arch/arm/mach-imx/time.c')
-rw-r--r-- | arch/arm/mach-imx/time.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c new file mode 100644 index 000000000000..f017302f6d09 --- /dev/null +++ b/arch/arm/mach-imx/time.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-mxc/time.c | ||
3 | * | ||
4 | * Copyright (C) 2000-2001 Deep Blue Solutions | ||
5 | * Copyright (C) 2002 Shane Nay (shane@minirl.com) | ||
6 | * Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) | ||
7 | * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
21 | * MA 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/clockchips.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/err.h> | ||
29 | |||
30 | #include <asm/sched_clock.h> | ||
31 | #include <asm/mach/time.h> | ||
32 | |||
33 | #include "common.h" | ||
34 | #include "hardware.h" | ||
35 | |||
36 | /* | ||
37 | * There are 2 versions of the timer hardware on Freescale MXC hardware. | ||
38 | * Version 1: MX1/MXL, MX21, MX27. | ||
39 | * Version 2: MX25, MX31, MX35, MX37, MX51 | ||
40 | */ | ||
41 | |||
42 | /* defines common for all i.MX */ | ||
43 | #define MXC_TCTL 0x00 | ||
44 | #define MXC_TCTL_TEN (1 << 0) /* Enable module */ | ||
45 | #define MXC_TPRER 0x04 | ||
46 | |||
47 | /* MX1, MX21, MX27 */ | ||
48 | #define MX1_2_TCTL_CLK_PCLK1 (1 << 1) | ||
49 | #define MX1_2_TCTL_IRQEN (1 << 4) | ||
50 | #define MX1_2_TCTL_FRR (1 << 8) | ||
51 | #define MX1_2_TCMP 0x08 | ||
52 | #define MX1_2_TCN 0x10 | ||
53 | #define MX1_2_TSTAT 0x14 | ||
54 | |||
55 | /* MX21, MX27 */ | ||
56 | #define MX2_TSTAT_CAPT (1 << 1) | ||
57 | #define MX2_TSTAT_COMP (1 << 0) | ||
58 | |||
59 | /* MX31, MX35, MX25, MX5 */ | ||
60 | #define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */ | ||
61 | #define V2_TCTL_CLK_IPG (1 << 6) | ||
62 | #define V2_TCTL_CLK_PER (2 << 6) | ||
63 | #define V2_TCTL_FRR (1 << 9) | ||
64 | #define V2_IR 0x0c | ||
65 | #define V2_TSTAT 0x08 | ||
66 | #define V2_TSTAT_OF1 (1 << 0) | ||
67 | #define V2_TCN 0x24 | ||
68 | #define V2_TCMP 0x10 | ||
69 | |||
70 | #define timer_is_v1() (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27()) | ||
71 | #define timer_is_v2() (!timer_is_v1()) | ||
72 | |||
73 | static struct clock_event_device clockevent_mxc; | ||
74 | static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED; | ||
75 | |||
76 | static void __iomem *timer_base; | ||
77 | |||
78 | static inline void gpt_irq_disable(void) | ||
79 | { | ||
80 | unsigned int tmp; | ||
81 | |||
82 | if (timer_is_v2()) | ||
83 | __raw_writel(0, timer_base + V2_IR); | ||
84 | else { | ||
85 | tmp = __raw_readl(timer_base + MXC_TCTL); | ||
86 | __raw_writel(tmp & ~MX1_2_TCTL_IRQEN, timer_base + MXC_TCTL); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static inline void gpt_irq_enable(void) | ||
91 | { | ||
92 | if (timer_is_v2()) | ||
93 | __raw_writel(1<<0, timer_base + V2_IR); | ||
94 | else { | ||
95 | __raw_writel(__raw_readl(timer_base + MXC_TCTL) | MX1_2_TCTL_IRQEN, | ||
96 | timer_base + MXC_TCTL); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static void gpt_irq_acknowledge(void) | ||
101 | { | ||
102 | if (timer_is_v1()) { | ||
103 | if (cpu_is_mx1()) | ||
104 | __raw_writel(0, timer_base + MX1_2_TSTAT); | ||
105 | else | ||
106 | __raw_writel(MX2_TSTAT_CAPT | MX2_TSTAT_COMP, | ||
107 | timer_base + MX1_2_TSTAT); | ||
108 | } else if (timer_is_v2()) | ||
109 | __raw_writel(V2_TSTAT_OF1, timer_base + V2_TSTAT); | ||
110 | } | ||
111 | |||
112 | static void __iomem *sched_clock_reg; | ||
113 | |||
114 | static u32 notrace mxc_read_sched_clock(void) | ||
115 | { | ||
116 | return sched_clock_reg ? __raw_readl(sched_clock_reg) : 0; | ||
117 | } | ||
118 | |||
119 | static int __init mxc_clocksource_init(struct clk *timer_clk) | ||
120 | { | ||
121 | unsigned int c = clk_get_rate(timer_clk); | ||
122 | void __iomem *reg = timer_base + (timer_is_v2() ? V2_TCN : MX1_2_TCN); | ||
123 | |||
124 | sched_clock_reg = reg; | ||
125 | |||
126 | setup_sched_clock(mxc_read_sched_clock, 32, c); | ||
127 | return clocksource_mmio_init(reg, "mxc_timer1", c, 200, 32, | ||
128 | clocksource_mmio_readl_up); | ||
129 | } | ||
130 | |||
131 | /* clock event */ | ||
132 | |||
133 | static int mx1_2_set_next_event(unsigned long evt, | ||
134 | struct clock_event_device *unused) | ||
135 | { | ||
136 | unsigned long tcmp; | ||
137 | |||
138 | tcmp = __raw_readl(timer_base + MX1_2_TCN) + evt; | ||
139 | |||
140 | __raw_writel(tcmp, timer_base + MX1_2_TCMP); | ||
141 | |||
142 | return (int)(tcmp - __raw_readl(timer_base + MX1_2_TCN)) < 0 ? | ||
143 | -ETIME : 0; | ||
144 | } | ||
145 | |||
146 | static int v2_set_next_event(unsigned long evt, | ||
147 | struct clock_event_device *unused) | ||
148 | { | ||
149 | unsigned long tcmp; | ||
150 | |||
151 | tcmp = __raw_readl(timer_base + V2_TCN) + evt; | ||
152 | |||
153 | __raw_writel(tcmp, timer_base + V2_TCMP); | ||
154 | |||
155 | return (int)(tcmp - __raw_readl(timer_base + V2_TCN)) < 0 ? | ||
156 | -ETIME : 0; | ||
157 | } | ||
158 | |||
159 | #ifdef DEBUG | ||
160 | static const char *clock_event_mode_label[] = { | ||
161 | [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", | ||
162 | [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT", | ||
163 | [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", | ||
164 | [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED", | ||
165 | [CLOCK_EVT_MODE_RESUME] = "CLOCK_EVT_MODE_RESUME", | ||
166 | }; | ||
167 | #endif /* DEBUG */ | ||
168 | |||
169 | static void mxc_set_mode(enum clock_event_mode mode, | ||
170 | struct clock_event_device *evt) | ||
171 | { | ||
172 | unsigned long flags; | ||
173 | |||
174 | /* | ||
175 | * The timer interrupt generation is disabled at least | ||
176 | * for enough time to call mxc_set_next_event() | ||
177 | */ | ||
178 | local_irq_save(flags); | ||
179 | |||
180 | /* Disable interrupt in GPT module */ | ||
181 | gpt_irq_disable(); | ||
182 | |||
183 | if (mode != clockevent_mode) { | ||
184 | /* Set event time into far-far future */ | ||
185 | if (timer_is_v2()) | ||
186 | __raw_writel(__raw_readl(timer_base + V2_TCN) - 3, | ||
187 | timer_base + V2_TCMP); | ||
188 | else | ||
189 | __raw_writel(__raw_readl(timer_base + MX1_2_TCN) - 3, | ||
190 | timer_base + MX1_2_TCMP); | ||
191 | |||
192 | /* Clear pending interrupt */ | ||
193 | gpt_irq_acknowledge(); | ||
194 | } | ||
195 | |||
196 | #ifdef DEBUG | ||
197 | printk(KERN_INFO "mxc_set_mode: changing mode from %s to %s\n", | ||
198 | clock_event_mode_label[clockevent_mode], | ||
199 | clock_event_mode_label[mode]); | ||
200 | #endif /* DEBUG */ | ||
201 | |||
202 | /* Remember timer mode */ | ||
203 | clockevent_mode = mode; | ||
204 | local_irq_restore(flags); | ||
205 | |||
206 | switch (mode) { | ||
207 | case CLOCK_EVT_MODE_PERIODIC: | ||
208 | printk(KERN_ERR"mxc_set_mode: Periodic mode is not " | ||
209 | "supported for i.MX\n"); | ||
210 | break; | ||
211 | case CLOCK_EVT_MODE_ONESHOT: | ||
212 | /* | ||
213 | * Do not put overhead of interrupt enable/disable into | ||
214 | * mxc_set_next_event(), the core has about 4 minutes | ||
215 | * to call mxc_set_next_event() or shutdown clock after | ||
216 | * mode switching | ||
217 | */ | ||
218 | local_irq_save(flags); | ||
219 | gpt_irq_enable(); | ||
220 | local_irq_restore(flags); | ||
221 | break; | ||
222 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
223 | case CLOCK_EVT_MODE_UNUSED: | ||
224 | case CLOCK_EVT_MODE_RESUME: | ||
225 | /* Left event sources disabled, no more interrupts appear */ | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * IRQ handler for the timer | ||
232 | */ | ||
233 | static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) | ||
234 | { | ||
235 | struct clock_event_device *evt = &clockevent_mxc; | ||
236 | uint32_t tstat; | ||
237 | |||
238 | if (timer_is_v2()) | ||
239 | tstat = __raw_readl(timer_base + V2_TSTAT); | ||
240 | else | ||
241 | tstat = __raw_readl(timer_base + MX1_2_TSTAT); | ||
242 | |||
243 | gpt_irq_acknowledge(); | ||
244 | |||
245 | evt->event_handler(evt); | ||
246 | |||
247 | return IRQ_HANDLED; | ||
248 | } | ||
249 | |||
250 | static struct irqaction mxc_timer_irq = { | ||
251 | .name = "i.MX Timer Tick", | ||
252 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
253 | .handler = mxc_timer_interrupt, | ||
254 | }; | ||
255 | |||
256 | static struct clock_event_device clockevent_mxc = { | ||
257 | .name = "mxc_timer1", | ||
258 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
259 | .shift = 32, | ||
260 | .set_mode = mxc_set_mode, | ||
261 | .set_next_event = mx1_2_set_next_event, | ||
262 | .rating = 200, | ||
263 | }; | ||
264 | |||
265 | static int __init mxc_clockevent_init(struct clk *timer_clk) | ||
266 | { | ||
267 | unsigned int c = clk_get_rate(timer_clk); | ||
268 | |||
269 | if (timer_is_v2()) | ||
270 | clockevent_mxc.set_next_event = v2_set_next_event; | ||
271 | |||
272 | clockevent_mxc.mult = div_sc(c, NSEC_PER_SEC, | ||
273 | clockevent_mxc.shift); | ||
274 | clockevent_mxc.max_delta_ns = | ||
275 | clockevent_delta2ns(0xfffffffe, &clockevent_mxc); | ||
276 | clockevent_mxc.min_delta_ns = | ||
277 | clockevent_delta2ns(0xff, &clockevent_mxc); | ||
278 | |||
279 | clockevent_mxc.cpumask = cpumask_of(0); | ||
280 | |||
281 | clockevents_register_device(&clockevent_mxc); | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | void __init mxc_timer_init(void __iomem *base, int irq) | ||
287 | { | ||
288 | uint32_t tctl_val; | ||
289 | struct clk *timer_clk; | ||
290 | struct clk *timer_ipg_clk; | ||
291 | |||
292 | timer_clk = clk_get_sys("imx-gpt.0", "per"); | ||
293 | if (IS_ERR(timer_clk)) { | ||
294 | pr_err("i.MX timer: unable to get clk\n"); | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | timer_ipg_clk = clk_get_sys("imx-gpt.0", "ipg"); | ||
299 | if (!IS_ERR(timer_ipg_clk)) | ||
300 | clk_prepare_enable(timer_ipg_clk); | ||
301 | |||
302 | clk_prepare_enable(timer_clk); | ||
303 | |||
304 | timer_base = base; | ||
305 | |||
306 | /* | ||
307 | * Initialise to a known state (all timers off, and timing reset) | ||
308 | */ | ||
309 | |||
310 | __raw_writel(0, timer_base + MXC_TCTL); | ||
311 | __raw_writel(0, timer_base + MXC_TPRER); /* see datasheet note */ | ||
312 | |||
313 | if (timer_is_v2()) | ||
314 | tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN; | ||
315 | else | ||
316 | tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN; | ||
317 | |||
318 | __raw_writel(tctl_val, timer_base + MXC_TCTL); | ||
319 | |||
320 | /* init and register the timer to the framework */ | ||
321 | mxc_clocksource_init(timer_clk); | ||
322 | mxc_clockevent_init(timer_clk); | ||
323 | |||
324 | /* Make irqs happen */ | ||
325 | setup_irq(irq, &mxc_timer_irq); | ||
326 | } | ||