diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-02 16:34:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-02 16:34:00 -0400 |
commit | 1810b6cb162e0c19e0ecbbacbcfd66f578f335ec (patch) | |
tree | 810494ca945483bf669a062d445d49d3bfb7d6a7 /arch/arm/plat-omap/timer32k.c | |
parent | ef7a4567dc542d8cc563755478464ea928fede41 (diff) | |
parent | 9b6553cd01ce3ea7a6a532f7b7e62e3535d6b102 (diff) |
Merge master.kernel.org:/home/rmk/linux-2.6-arm
* master.kernel.org:/home/rmk/linux-2.6-arm: (27 commits)
[ARM] 3433/1: ARM: OMAP: 8/8 Update board files
[ARM] 3455/1: ARM: OMAP: 7/8 Misc updates, take 2
[ARM] 3454/1: ARM: OMAP: 6/8 Update framebuffer low-level init code, take 2
[ARM] 3430/1: ARM: OMAP: 5/8 Update PM
[ARM] 3429/1: ARM: OMAP: 4/8 Update GPIO
[ARM] 3428/1: ARM: OMAP: 3/8 Update pin multiplexing
[ARM] 3427/1: ARM: OMAP: 2/8 Update timers
[ARM] 3426/1: ARM: OMAP: 1/8 Update clock framework
[ARM] 3396/2: AT91RM9200 Platform devices update
[ARM] 3395/2: AT91RM9200 Dataflash Card vs MMC selection
[ARM] 3393/2: AT91RM9200 LED support
[ARM] 3453/1: Poodle: Correctly set the memory size
[ARM] 3446/1: i.MX: MMC/SD SDHC controller registration for i.MX/MX1 MX1ADS board
[ARM] 3444/1: i.MX: Scatter-gather DMA emulation for i.MX/MX1
[ARM] 3451/1: ep93xx: use the m48t86 rtc driver on the ts72xx platform
[ARM] 3450/1: ep93xx: use the ep93xx rtc driver
[ARM] 3452/1: [S3C2410] RX3715 - add nand information
[ARM] 3449/1: [S3C2410] Anubis - fix NAND timings
[ARM] 3448/1: [S3C2410] Settle delay when _enabling_ USB PLL
[ARM] 3442/1: [S3C2410] SMDK: NAND device setup
...
Diffstat (limited to 'arch/arm/plat-omap/timer32k.c')
-rw-r--r-- | arch/arm/plat-omap/timer32k.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/timer32k.c b/arch/arm/plat-omap/timer32k.c new file mode 100644 index 000000000000..b2a943bf11ef --- /dev/null +++ b/arch/arm/plat-omap/timer32k.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-omap/timer32k.c | ||
3 | * | ||
4 | * OMAP 32K Timer | ||
5 | * | ||
6 | * Copyright (C) 2004 - 2005 Nokia Corporation | ||
7 | * Partial timer rewrite and additional dynamic tick timer support by | ||
8 | * Tony Lindgen <tony@atomide.com> and | ||
9 | * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
10 | * | ||
11 | * MPU timer code based on the older MPU timer code for OMAP | ||
12 | * Copyright (C) 2000 RidgeRun, Inc. | ||
13 | * Author: Greg Lonnon <glonnon@ridgerun.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License as published by the | ||
17 | * Free Software Foundation; either version 2 of the License, or (at your | ||
18 | * option) any later version. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
21 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
23 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
26 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
30 | * | ||
31 | * You should have received a copy of the GNU General Public License along | ||
32 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
33 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
34 | */ | ||
35 | |||
36 | #include <linux/config.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/spinlock.h> | ||
43 | #include <linux/err.h> | ||
44 | #include <linux/clk.h> | ||
45 | |||
46 | #include <asm/system.h> | ||
47 | #include <asm/hardware.h> | ||
48 | #include <asm/io.h> | ||
49 | #include <asm/leds.h> | ||
50 | #include <asm/irq.h> | ||
51 | #include <asm/mach/irq.h> | ||
52 | #include <asm/mach/time.h> | ||
53 | |||
54 | struct sys_timer omap_timer; | ||
55 | |||
56 | /* | ||
57 | * --------------------------------------------------------------------------- | ||
58 | * 32KHz OS timer | ||
59 | * | ||
60 | * This currently works only on 16xx, as 1510 does not have the continuous | ||
61 | * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track | ||
62 | * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer | ||
63 | * on 1510 would be possible, but the timer would not be as accurate as | ||
64 | * with the 32KHz synchronized timer. | ||
65 | * --------------------------------------------------------------------------- | ||
66 | */ | ||
67 | |||
68 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
69 | #define TIMER_32K_SYNCHRONIZED 0xfffbc410 | ||
70 | #elif defined(CONFIG_ARCH_OMAP24XX) | ||
71 | #define TIMER_32K_SYNCHRONIZED 0x48004010 | ||
72 | #else | ||
73 | #error OMAP 32KHz timer does not currently work on 15XX! | ||
74 | #endif | ||
75 | |||
76 | /* 16xx specific defines */ | ||
77 | #define OMAP1_32K_TIMER_BASE 0xfffb9000 | ||
78 | #define OMAP1_32K_TIMER_CR 0x08 | ||
79 | #define OMAP1_32K_TIMER_TVR 0x00 | ||
80 | #define OMAP1_32K_TIMER_TCR 0x04 | ||
81 | |||
82 | /* 24xx specific defines */ | ||
83 | #define OMAP2_GP_TIMER_BASE 0x48028000 | ||
84 | #define CM_CLKSEL_WKUP 0x48008440 | ||
85 | #define GP_TIMER_TIDR 0x00 | ||
86 | #define GP_TIMER_TISR 0x18 | ||
87 | #define GP_TIMER_TIER 0x1c | ||
88 | #define GP_TIMER_TCLR 0x24 | ||
89 | #define GP_TIMER_TCRR 0x28 | ||
90 | #define GP_TIMER_TLDR 0x2c | ||
91 | #define GP_TIMER_TTGR 0x30 | ||
92 | #define GP_TIMER_TSICR 0x40 | ||
93 | |||
94 | #define OMAP_32K_TICKS_PER_HZ (32768 / HZ) | ||
95 | |||
96 | /* | ||
97 | * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 | ||
98 | * so with HZ = 128, TVR = 255. | ||
99 | */ | ||
100 | #define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) | ||
101 | |||
102 | #define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ | ||
103 | (((nr_jiffies) * (clock_rate)) / HZ) | ||
104 | |||
105 | static inline void omap_32k_timer_write(int val, int reg) | ||
106 | { | ||
107 | if (cpu_class_is_omap1()) | ||
108 | omap_writew(val, OMAP1_32K_TIMER_BASE + reg); | ||
109 | |||
110 | if (cpu_is_omap24xx()) | ||
111 | omap_writel(val, OMAP2_GP_TIMER_BASE + reg); | ||
112 | } | ||
113 | |||
114 | static inline unsigned long omap_32k_timer_read(int reg) | ||
115 | { | ||
116 | if (cpu_class_is_omap1()) | ||
117 | return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff; | ||
118 | |||
119 | if (cpu_is_omap24xx()) | ||
120 | return omap_readl(OMAP2_GP_TIMER_BASE + reg); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * The 32KHz synchronized timer is an additional timer on 16xx. | ||
125 | * It is always running. | ||
126 | */ | ||
127 | static inline unsigned long omap_32k_sync_timer_read(void) | ||
128 | { | ||
129 | return omap_readl(TIMER_32K_SYNCHRONIZED); | ||
130 | } | ||
131 | |||
132 | static inline void omap_32k_timer_start(unsigned long load_val) | ||
133 | { | ||
134 | if (cpu_class_is_omap1()) { | ||
135 | omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); | ||
136 | omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); | ||
137 | } | ||
138 | |||
139 | if (cpu_is_omap24xx()) { | ||
140 | omap_32k_timer_write(0xffffffff - load_val, GP_TIMER_TCRR); | ||
141 | omap_32k_timer_write((1 << 1), GP_TIMER_TIER); | ||
142 | omap_32k_timer_write((1 << 1) | 1, GP_TIMER_TCLR); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | static inline void omap_32k_timer_stop(void) | ||
147 | { | ||
148 | if (cpu_class_is_omap1()) | ||
149 | omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); | ||
150 | |||
151 | if (cpu_is_omap24xx()) | ||
152 | omap_32k_timer_write(0x0, GP_TIMER_TCLR); | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Rounds down to nearest usec. Note that this will overflow for larger values. | ||
157 | */ | ||
158 | static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) | ||
159 | { | ||
160 | return (ticks_32k * 5*5*5*5*5*5) >> 9; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Rounds down to nearest nsec. | ||
165 | */ | ||
166 | static inline unsigned long long | ||
167 | omap_32k_ticks_to_nsecs(unsigned long ticks_32k) | ||
168 | { | ||
169 | return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; | ||
170 | } | ||
171 | |||
172 | static unsigned long omap_32k_last_tick = 0; | ||
173 | |||
174 | /* | ||
175 | * Returns elapsed usecs since last 32k timer interrupt | ||
176 | */ | ||
177 | static unsigned long omap_32k_timer_gettimeoffset(void) | ||
178 | { | ||
179 | unsigned long now = omap_32k_sync_timer_read(); | ||
180 | return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Returns current time from boot in nsecs. It's OK for this to wrap | ||
185 | * around for now, as it's just a relative time stamp. | ||
186 | */ | ||
187 | unsigned long long sched_clock(void) | ||
188 | { | ||
189 | return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this | ||
194 | * function is also called from other interrupts to remove latency | ||
195 | * issues with dynamic tick. In the dynamic tick case, we need to lock | ||
196 | * with irqsave. | ||
197 | */ | ||
198 | static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, | ||
199 | struct pt_regs *regs) | ||
200 | { | ||
201 | unsigned long flags; | ||
202 | unsigned long now; | ||
203 | |||
204 | write_seqlock_irqsave(&xtime_lock, flags); | ||
205 | |||
206 | if (cpu_is_omap24xx()) { | ||
207 | u32 status = omap_32k_timer_read(GP_TIMER_TISR); | ||
208 | omap_32k_timer_write(status, GP_TIMER_TISR); | ||
209 | } | ||
210 | |||
211 | now = omap_32k_sync_timer_read(); | ||
212 | |||
213 | while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { | ||
214 | omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; | ||
215 | timer_tick(regs); | ||
216 | } | ||
217 | |||
218 | /* Restart timer so we don't drift off due to modulo or dynamic tick. | ||
219 | * By default we program the next timer to be continuous to avoid | ||
220 | * latencies during high system load. During dynamic tick operation the | ||
221 | * continuous timer can be overridden from pm_idle to be longer. | ||
222 | */ | ||
223 | omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); | ||
224 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
225 | |||
226 | return IRQ_HANDLED; | ||
227 | } | ||
228 | |||
229 | #ifdef CONFIG_NO_IDLE_HZ | ||
230 | /* | ||
231 | * Programs the next timer interrupt needed. Called when dynamic tick is | ||
232 | * enabled, and to reprogram the ticks to skip from pm_idle. Note that | ||
233 | * we can keep the timer continuous, and don't need to set it to run in | ||
234 | * one-shot mode. This is because the timer will get reprogrammed again | ||
235 | * after next interrupt. | ||
236 | */ | ||
237 | void omap_32k_timer_reprogram(unsigned long next_tick) | ||
238 | { | ||
239 | omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); | ||
240 | } | ||
241 | |||
242 | static struct irqaction omap_32k_timer_irq; | ||
243 | extern struct timer_update_handler timer_update; | ||
244 | |||
245 | static int omap_32k_timer_enable_dyn_tick(void) | ||
246 | { | ||
247 | /* No need to reprogram timer, just use the next interrupt */ | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int omap_32k_timer_disable_dyn_tick(void) | ||
252 | { | ||
253 | omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static struct dyn_tick_timer omap_dyn_tick_timer = { | ||
258 | .enable = omap_32k_timer_enable_dyn_tick, | ||
259 | .disable = omap_32k_timer_disable_dyn_tick, | ||
260 | .reprogram = omap_32k_timer_reprogram, | ||
261 | .handler = omap_32k_timer_interrupt, | ||
262 | }; | ||
263 | #endif /* CONFIG_NO_IDLE_HZ */ | ||
264 | |||
265 | static struct irqaction omap_32k_timer_irq = { | ||
266 | .name = "32KHz timer", | ||
267 | .flags = SA_INTERRUPT | SA_TIMER, | ||
268 | .handler = omap_32k_timer_interrupt, | ||
269 | }; | ||
270 | |||
271 | static struct clk * gpt1_ick; | ||
272 | static struct clk * gpt1_fck; | ||
273 | |||
274 | static __init void omap_init_32k_timer(void) | ||
275 | { | ||
276 | #ifdef CONFIG_NO_IDLE_HZ | ||
277 | omap_timer.dyn_tick = &omap_dyn_tick_timer; | ||
278 | #endif | ||
279 | |||
280 | if (cpu_class_is_omap1()) | ||
281 | setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); | ||
282 | if (cpu_is_omap24xx()) | ||
283 | setup_irq(37, &omap_32k_timer_irq); | ||
284 | omap_timer.offset = omap_32k_timer_gettimeoffset; | ||
285 | omap_32k_last_tick = omap_32k_sync_timer_read(); | ||
286 | |||
287 | /* REVISIT: Check 24xx TIOCP_CFG settings after idle works */ | ||
288 | if (cpu_is_omap24xx()) { | ||
289 | omap_32k_timer_write(0, GP_TIMER_TCLR); | ||
290 | omap_writel(0, CM_CLKSEL_WKUP); /* 32KHz clock source */ | ||
291 | |||
292 | gpt1_ick = clk_get(NULL, "gpt1_ick"); | ||
293 | if (IS_ERR(gpt1_ick)) | ||
294 | printk(KERN_ERR "Could not get gpt1_ick\n"); | ||
295 | else | ||
296 | clk_enable(gpt1_ick); | ||
297 | |||
298 | gpt1_fck = clk_get(NULL, "gpt1_fck"); | ||
299 | if (IS_ERR(gpt1_fck)) | ||
300 | printk(KERN_ERR "Could not get gpt1_fck\n"); | ||
301 | else | ||
302 | clk_enable(gpt1_fck); | ||
303 | |||
304 | mdelay(100); /* Wait for clocks to stabilize */ | ||
305 | |||
306 | omap_32k_timer_write(0x7, GP_TIMER_TISR); | ||
307 | } | ||
308 | |||
309 | omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * --------------------------------------------------------------------------- | ||
314 | * Timer initialization | ||
315 | * --------------------------------------------------------------------------- | ||
316 | */ | ||
317 | static void __init omap_timer_init(void) | ||
318 | { | ||
319 | omap_init_32k_timer(); | ||
320 | } | ||
321 | |||
322 | struct sys_timer omap_timer = { | ||
323 | .init = omap_timer_init, | ||
324 | .offset = NULL, /* Initialized later */ | ||
325 | }; | ||