m68knommu: Add Coldfire DMA Timer support
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / arch / arm / plat-mxc / time.c
blob3bf86343fdf41059890ed0e187223c437541051c
1 /*
2 * linux/arch/arm/plat-mxc/time.c
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)
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.
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.
24 #include <linux/interrupt.h>
25 #include <linux/irq.h>
26 #include <linux/clockchips.h>
27 #include <linux/clk.h>
29 #include <asm/hardware.h>
30 #include <asm/mach/time.h>
31 #include <asm/arch/common.h>
32 #include <asm/arch/mxc_timer.h>
34 static struct clock_event_device clockevent_mxc;
35 static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED;
37 /* clock source for the timer */
38 static struct clk *timer_clk;
40 /* clock source */
42 static cycle_t mxc_get_cycles(void)
44 return __raw_readl(TIMER_BASE + MXC_TCN);
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,
56 static int __init mxc_clocksource_init(void)
58 unsigned int clock;
60 clock = clk_get_rate(timer_clk);
62 clocksource_mxc.mult = clocksource_hz2mult(clock,
63 clocksource_mxc.shift);
64 clocksource_register(&clocksource_mxc);
66 return 0;
69 /* clock event */
71 static int mxc_set_next_event(unsigned long evt,
72 struct clock_event_device *unused)
74 unsigned long tcmp;
76 tcmp = __raw_readl(TIMER_BASE + MXC_TCN) + evt;
77 __raw_writel(tcmp, TIMER_BASE + MXC_TCMP);
79 return (int)(tcmp - __raw_readl(TIMER_BASE + MXC_TCN)) < 0 ?
80 -ETIME : 0;
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"
90 #endif /* DEBUG */
92 static void mxc_set_mode(enum clock_event_mode mode,
93 struct clock_event_device *evt)
95 unsigned long flags;
98 * The timer interrupt generation is disabled at least
99 * for enough time to call mxc_set_next_event()
101 local_irq_save(flags);
103 /* Disable interrupt in GPT module */
104 gpt_irq_disable();
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();
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 */
120 /* Remember timer mode */
121 clockevent_mode = mode;
122 local_irq_restore(flags);
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:
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
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;
149 * IRQ handler for the timer
151 static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
153 struct clock_event_device *evt = &clockevent_mxc;
154 uint32_t tstat;
156 tstat = __raw_readl(TIMER_BASE + MXC_TSTAT);
158 gpt_irq_acknowledge();
160 evt->event_handler(evt);
162 return IRQ_HANDLED;
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,
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,
180 static int __init mxc_clockevent_init(void)
182 unsigned int clock;
184 clock = clk_get_rate(timer_clk);
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);
193 clockevent_mxc.cpumask = cpumask_of_cpu(0);
195 clockevents_register_device(&clockevent_mxc);
197 return 0;
200 void __init mxc_timer_init(const char *clk_timer)
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;
208 clk_enable(timer_clk);
211 * Initialise to a known state (all timers off, and timing reset)
213 __raw_writel(0, TIMER_BASE + MXC_TCTL);
214 __raw_writel(0, TIMER_BASE + MXC_TPRER); /* see datasheet note */
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);
221 /* init and register the timer to the framework */
222 mxc_clocksource_init();
223 mxc_clockevent_init();
225 /* Make irqs happen */
226 setup_irq(TIMER_INTERRUPT, &mxc_timer_irq);