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