Merge branch 'percpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6] / arch / arm / plat-mxc / time.c
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
29 #include <mach/hardware.h>
30 #include <asm/mach/time.h>
31 #include <mach/common.h>
32 #include <mach/mxc_timer.h>
33
34 static struct clock_event_device clockevent_mxc;
35 static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED;
36
37 /* clock source */
38
39 static cycle_t mxc_get_cycles(struct clocksource *cs)
40 {
41         return __raw_readl(TIMER_BASE + MXC_TCN);
42 }
43
44 static 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
53 static int __init mxc_clocksource_init(struct clk *timer_clk)
54 {
55         unsigned int c = clk_get_rate(timer_clk);
56
57         clocksource_mxc.mult = clocksource_hz2mult(c,
58                                         clocksource_mxc.shift);
59         clocksource_register(&clocksource_mxc);
60
61         return 0;
62 }
63
64 /* clock event */
65
66 static 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
79 static 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
87 static 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  */
146 static 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
160 static 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
166 static 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
175 static int __init mxc_clockevent_init(struct clk *timer_clk)
176 {
177         unsigned int c = clk_get_rate(timer_clk);
178
179         clockevent_mxc.mult = div_sc(c, NSEC_PER_SEC,
180                                         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
186         clockevent_mxc.cpumask = cpumask_of(0);
187
188         clockevents_register_device(&clockevent_mxc);
189
190         return 0;
191 }
192
193 void __init mxc_timer_init(struct clk *timer_clk)
194 {
195         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 */
209         mxc_clocksource_init(timer_clk);
210         mxc_clockevent_init(timer_clk);
211
212         /* Make irqs happen */
213         setup_irq(TIMER_INTERRUPT, &mxc_timer_irq);
214 }