blob: eb93fd1789db63aefd765130e5caa0646077cb84 [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
39static cycle_t mxc_get_cycles(void)
40{
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{
55 unsigned int clock;
56
57 clock = clk_get_rate(timer_clk);
58
59 clocksource_mxc.mult = clocksource_hz2mult(clock,
60 clocksource_mxc.shift);
61 clocksource_register(&clocksource_mxc);
62
63 return 0;
64}
65
66/* clock event */
67
68static int mxc_set_next_event(unsigned long evt,
69 struct clock_event_device *unused)
70{
71 unsigned long tcmp;
72
73 tcmp = __raw_readl(TIMER_BASE + MXC_TCN) + evt;
74 __raw_writel(tcmp, TIMER_BASE + MXC_TCMP);
75
76 return (int)(tcmp - __raw_readl(TIMER_BASE + MXC_TCN)) < 0 ?
77 -ETIME : 0;
78}
79
80#ifdef DEBUG
81static const char *clock_event_mode_label[] = {
82 [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC",
83 [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT",
84 [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN",
85 [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED"
86};
87#endif /* DEBUG */
88
89static void mxc_set_mode(enum clock_event_mode mode,
90 struct clock_event_device *evt)
91{
92 unsigned long flags;
93
94 /*
95 * The timer interrupt generation is disabled at least
96 * for enough time to call mxc_set_next_event()
97 */
98 local_irq_save(flags);
99
100 /* Disable interrupt in GPT module */
101 gpt_irq_disable();
102
103 if (mode != clockevent_mode) {
104 /* Set event time into far-far future */
105 __raw_writel(__raw_readl(TIMER_BASE + MXC_TCN) - 3,
106 TIMER_BASE + MXC_TCMP);
107 /* Clear pending interrupt */
108 gpt_irq_acknowledge();
109 }
110
111#ifdef DEBUG
112 printk(KERN_INFO "mxc_set_mode: changing mode from %s to %s\n",
113 clock_event_mode_label[clockevent_mode],
114 clock_event_mode_label[mode]);
115#endif /* DEBUG */
116
117 /* Remember timer mode */
118 clockevent_mode = mode;
119 local_irq_restore(flags);
120
121 switch (mode) {
122 case CLOCK_EVT_MODE_PERIODIC:
123 printk(KERN_ERR"mxc_set_mode: Periodic mode is not "
124 "supported for i.MX\n");
125 break;
126 case CLOCK_EVT_MODE_ONESHOT:
127 /*
128 * Do not put overhead of interrupt enable/disable into
129 * mxc_set_next_event(), the core has about 4 minutes
130 * to call mxc_set_next_event() or shutdown clock after
131 * mode switching
132 */
133 local_irq_save(flags);
134 gpt_irq_enable();
135 local_irq_restore(flags);
136 break;
137 case CLOCK_EVT_MODE_SHUTDOWN:
138 case CLOCK_EVT_MODE_UNUSED:
139 case CLOCK_EVT_MODE_RESUME:
140 /* Left event sources disabled, no more interrupts appear */
141 break;
142 }
143}
144
145/*
146 * IRQ handler for the timer
147 */
148static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
149{
150 struct clock_event_device *evt = &clockevent_mxc;
151 uint32_t tstat;
152
153 tstat = __raw_readl(TIMER_BASE + MXC_TSTAT);
154
155 gpt_irq_acknowledge();
156
157 evt->event_handler(evt);
158
159 return IRQ_HANDLED;
160}
161
162static struct irqaction mxc_timer_irq = {
163 .name = "i.MX Timer Tick",
164 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
165 .handler = mxc_timer_interrupt,
166};
167
168static struct clock_event_device clockevent_mxc = {
169 .name = "mxc_timer1",
170 .features = CLOCK_EVT_FEAT_ONESHOT,
171 .shift = 32,
172 .set_mode = mxc_set_mode,
173 .set_next_event = mxc_set_next_event,
174 .rating = 200,
175};
176
Sascha Hauer30c730f2009-02-16 14:36:49 +0100177static int __init mxc_clockevent_init(struct clk *timer_clk)
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200178{
179 unsigned int clock;
180
181 clock = clk_get_rate(timer_clk);
182
183 clockevent_mxc.mult = div_sc(clock, NSEC_PER_SEC,
184 clockevent_mxc.shift);
185 clockevent_mxc.max_delta_ns =
186 clockevent_delta2ns(0xfffffffe, &clockevent_mxc);
187 clockevent_mxc.min_delta_ns =
188 clockevent_delta2ns(0xff, &clockevent_mxc);
189
Rusty Russell320ab2b2008-12-13 21:20:26 +1030190 clockevent_mxc.cpumask = cpumask_of(0);
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200191
192 clockevents_register_device(&clockevent_mxc);
193
194 return 0;
195}
196
Sascha Hauer30c730f2009-02-16 14:36:49 +0100197void __init mxc_timer_init(struct clk *timer_clk)
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200198{
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200199 clk_enable(timer_clk);
200
201 /*
202 * Initialise to a known state (all timers off, and timing reset)
203 */
204 __raw_writel(0, TIMER_BASE + MXC_TCTL);
205 __raw_writel(0, TIMER_BASE + MXC_TPRER); /* see datasheet note */
206
207 __raw_writel(TCTL_FRR | /* free running */
208 TCTL_VAL | /* set clocksource and arch specific bits */
209 TCTL_TEN, /* start the timer */
210 TIMER_BASE + MXC_TCTL);
211
212 /* init and register the timer to the framework */
Sascha Hauer30c730f2009-02-16 14:36:49 +0100213 mxc_clocksource_init(timer_clk);
214 mxc_clockevent_init(timer_clk);
Juergen Beisertd0f349f2008-07-05 10:02:50 +0200215
216 /* Make irqs happen */
217 setup_irq(TIMER_INTERRUPT, &mxc_timer_irq);
218}