• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mxc_get_cycles(void)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 
mxc_clocksource_init(void)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 
mxc_set_next_event(unsigned long evt,struct clock_event_device * unused)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 
mxc_set_mode(enum clock_event_mode mode,struct clock_event_device * evt)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  */
mxc_timer_interrupt(int irq,void * dev_id)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 
mxc_clockevent_init(void)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(0);
194 
195 	clockevents_register_device(&clockevent_mxc);
196 
197 	return 0;
198 }
199 
mxc_timer_init(const char * clk_timer)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 
229