blob: dab3357196fb8a6b6c51f71329b0cb15ce1f4932 [file] [log] [blame]
Juergen Beisertd0f349f2008-07-05 10:02:50 +02001/*
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
Russell Kinga09e64f2008-08-05 16:14:15 +010029#include <mach/hardware.h>
Juergen Beisertd0f349f2008-07-05 10:02:50 +020030#include <asm/mach/time.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010031#include <mach/common.h>
32#include <mach/mxc_timer.h>
Juergen Beisertd0f349f2008-07-05 10:02:50 +020033
34static struct clock_event_device clockevent_mxc;
35static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED;
36
Juergen Beisertd0f349f2008-07-05 10:02:50 +020037/* clock source */
38
Magnus Damm8e196082009-04-21 12:24:00 -070039static cycle_t mxc_get_cycles(struct clocksource *cs)
Juergen Beisertd0f349f2008-07-05 10:02:50 +020040{
41 return __raw_readl(TIMER_BASE + MXC_TCN);
42}
43
44static struct clocksource clocksource_mxc = {
45 .name = "mxc_timer1",
46 .rating = 200,
47 .read = mxc_get_cycles,
48 .mask = CLOCKSOURCE_MASK(32),
49 .shift = 20,
50 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
51};
52
Sascha Hauer30c730f2009-02-16 14:36:49 +010053static int __init mxc_clocksource_init(struct clk *timer_clk)
Juergen Beisertd0f349f2008-07-05 10:02:50 +020054{
Holger Schurig058b7a62009-01-26 16:34:51 +010055 unsigned int c = clk_get_rate(timer_clk);
Juergen Beisertd0f349f2008-07-05 10:02:50 +020056
Holger Schurig058b7a62009-01-26 16:34:51 +010057 clocksource_mxc.mult = clocksource_hz2mult(c,
Juergen Beisertd0f349f2008-07-05 10:02:50 +020058 clocksource_mxc.shift);
59 clocksource_register(&clocksource_mxc);
60
61 return 0;
62}
63
64/* clock event */
65
66static int mxc_set_next_event(unsigned long evt,
67 struct clock_event_device *unused)
68{
69 unsigned long tcmp;
70
71 tcmp = __raw_readl(TIMER_BASE + MXC_TCN) + evt;
72 __raw_writel(tcmp, TIMER_BASE + MXC_TCMP);
73
74 return (int)(tcmp - __raw_readl(TIMER_BASE + MXC_TCN)) < 0 ?
75 -ETIME : 0;
76}
77
78#ifdef DEBUG
79static const char *clock_event_mode_label[] = {
80 [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC",
81 [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT",
82 [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN",
83 [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED"
84};
85#endif /* DEBUG */
86
87static void mxc_set_mode(enum clock_event_mode mode,
88 struct clock_event_device *evt)
89{
90 unsigned long flags;
91
92 /*
93 * The timer interrupt generation is disabled at least
94 * for enough time to call mxc_set_next_event()
95 */
96 local_irq_save(flags);
97
98 /* Disable interrupt in GPT module */
99 gpt_irq_disable();
100
101 if (mode != clockevent_mode) {
102 /* Set event time into far-far future */
103 __raw_writel(__raw_readl(TIMER_BASE + MXC_TCN) - 3,
104 TIMER_BASE + MXC_TCMP);
105 /* Clear pending interrupt */
106 gpt_irq_acknowledge();
107 }
108
109#ifdef DEBUG
110 printk(KERN_INFO "mxc_set_mode: changing mode from %s to %s\n",
111 clock_event_mode_label[clockevent_mode],
112 clock_event_mode_label[mode]);
113#endif /* DEBUG */
114
115 /* Remember timer mode */
116 clockevent_mode = mode;
117 local_irq_restore(flags);
118
119 switch (mode) {
120 case CLOCK_EVT_MODE_PERIODIC:
121 printk(KERN_ERR"mxc_set_mode: Periodic mode is not "
122 "supported for i.MX\n");
123 break;
124 case CLOCK_EVT_MODE_ONESHOT:
125 /*
126 * Do not put overhead of interrupt enable/disable into
127 * mxc_set_next_event(), the core has about 4 minutes
128 * to call mxc_set_next_event() or shutdown clock after
129 * mode switching
130 */
131 local_irq_save(flags);
132 gpt_irq_enable();
133 local_irq_restore(flags);
134 break;
135 case CLOCK_EVT_MODE_SHUTDOWN:
136 case CLOCK_EVT_MODE_UNUSED:
137 case CLOCK_EVT_MODE_RESUME:
138 /* Left event sources disabled, no more interrupts appear */
139 break;
140 }
141}
142
143/*
144 * IRQ handler for the timer
145 */
146static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
147{
148 struct clock_event_device *evt = &clockevent_mxc;
149 uint32_t tstat;
150
151 tstat = __raw_readl(TIMER_BASE + MXC_TSTAT);
152
153 gpt_irq_acknowledge();
154
155 evt->event_handler(evt);
156
157 return IRQ_HANDLED;
158}
159
160static struct irqaction mxc_timer_irq = {
161 .name = "i.MX Timer Tick",
162 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
163 .handler = mxc_timer_interrupt,
164};
165
166static struct clock_event_device clockevent_mxc = {
167 .name = "mxc_timer1",
168 .features = CLOCK_EVT_FEAT_ONESHOT,
169 .shift = 32,
170 .set_mode = mxc_set_mode,
171 .set_next_event = mxc_set_next_event,
172 .rating = 200,
173};
174
Sascha Hauer30c730f2009-02-16 14:36:49 +0100175static int __init mxc_clockevent_init(struct clk *timer_clk)
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200176{
Holger Schurig058b7a62009-01-26 16:34:51 +0100177 unsigned int c = clk_get_rate(timer_clk);
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200178
Holger Schurig058b7a62009-01-26 16:34:51 +0100179 clockevent_mxc.mult = div_sc(c, NSEC_PER_SEC,
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200180 clockevent_mxc.shift);
181 clockevent_mxc.max_delta_ns =
182 clockevent_delta2ns(0xfffffffe, &clockevent_mxc);
183 clockevent_mxc.min_delta_ns =
184 clockevent_delta2ns(0xff, &clockevent_mxc);
185
Rusty Russell320ab2b2008-12-13 21:20:26 +1030186 clockevent_mxc.cpumask = cpumask_of(0);
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200187
188 clockevents_register_device(&clockevent_mxc);
189
190 return 0;
191}
192
Sascha Hauer30c730f2009-02-16 14:36:49 +0100193void __init mxc_timer_init(struct clk *timer_clk)
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200194{
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200195 clk_enable(timer_clk);
196
197 /*
198 * Initialise to a known state (all timers off, and timing reset)
199 */
200 __raw_writel(0, TIMER_BASE + MXC_TCTL);
201 __raw_writel(0, TIMER_BASE + MXC_TPRER); /* see datasheet note */
202
203 __raw_writel(TCTL_FRR | /* free running */
204 TCTL_VAL | /* set clocksource and arch specific bits */
205 TCTL_TEN, /* start the timer */
206 TIMER_BASE + MXC_TCTL);
207
208 /* init and register the timer to the framework */
Sascha Hauer30c730f2009-02-16 14:36:49 +0100209 mxc_clocksource_init(timer_clk);
210 mxc_clockevent_init(timer_clk);
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200211
212 /* Make irqs happen */
213 setup_irq(TIMER_INTERRUPT, &mxc_timer_irq);
214}