diff options
author | Tony Lindgren <tony@atomide.com> | 2006-04-02 12:46:21 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-04-02 12:46:21 -0400 |
commit | a569c6ec37e78eef4299d0a60ae4028459b27117 (patch) | |
tree | 6f51fde75ecd91661e1fc3268cdea47c47f4dd74 /arch/arm/plat-omap | |
parent | b824efae120b656fef562b2e81e1ed6aa88f8d24 (diff) |
[ARM] 3427/1: ARM: OMAP: 2/8 Update timers
Patch from Tony Lindgren
Update OMAP timers from linux-omap tree. The highlights of the
patch are:
- Move timer32k code from mach-omap1 to plat-omap and make it
work also on omap24xx by Tony Lindgren
- Add support for dmtimer idle check for PM by Tuukka Tikkanen
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 26 | ||||
-rw-r--r-- | arch/arm/plat-omap/timer32k.c | 325 |
3 files changed, 353 insertions, 2 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 0887bb2a2551..ec49495e651e 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -70,13 +70,13 @@ config OMAP_MPU_TIMER | |||
70 | 70 | ||
71 | config OMAP_32K_TIMER | 71 | config OMAP_32K_TIMER |
72 | bool "Use 32KHz timer" | 72 | bool "Use 32KHz timer" |
73 | depends on ARCH_OMAP16XX | 73 | depends on ARCH_OMAP16XX || ARCH_OMAP24XX |
74 | help | 74 | help |
75 | Select this option if you want to enable the OMAP 32KHz timer. | 75 | Select this option if you want to enable the OMAP 32KHz timer. |
76 | This timer saves power compared to the OMAP_MPU_TIMER, and has | 76 | This timer saves power compared to the OMAP_MPU_TIMER, and has |
77 | support for no tick during idle. The 32KHz timer provides less | 77 | support for no tick during idle. The 32KHz timer provides less |
78 | intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is | 78 | intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is |
79 | currently only available for OMAP-16xx. | 79 | currently only available for OMAP16XX and 24XX. |
80 | 80 | ||
81 | endchoice | 81 | endchoice |
82 | 82 | ||
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 38d7ebf87920..eba3cb52ad87 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c | |||
@@ -97,6 +97,32 @@ int omap_dm_timers_active(void) | |||
97 | } | 97 | } |
98 | 98 | ||
99 | 99 | ||
100 | /** | ||
101 | * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR | ||
102 | * @inputmask: current value of idlect mask | ||
103 | */ | ||
104 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) | ||
105 | { | ||
106 | int n; | ||
107 | |||
108 | /* If ARMXOR cannot be idled this function call is unnecessary */ | ||
109 | if (!(inputmask & (1 << 1))) | ||
110 | return inputmask; | ||
111 | |||
112 | /* If any active timer is using ARMXOR return modified mask */ | ||
113 | for (n = 0; dm_timers[n].base; ++n) | ||
114 | if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)& | ||
115 | OMAP_TIMER_CTRL_ST) { | ||
116 | if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0) | ||
117 | inputmask &= ~(1 << 1); | ||
118 | else | ||
119 | inputmask &= ~(1 << 2); | ||
120 | } | ||
121 | |||
122 | return inputmask; | ||
123 | } | ||
124 | |||
125 | |||
100 | void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) | 126 | void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
101 | { | 127 | { |
102 | int n = (timer - dm_timers) << 1; | 128 | int n = (timer - dm_timers) << 1; |
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 | }; | ||