]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - arch/arm/plat-mxc/time.c
ALSA: Add missing description of lx6464es to ALSA-Configuration.txt
[mirror_ubuntu-artful-kernel.git] / 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(void)
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 }