aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-omap
diff options
context:
space:
mode:
authorKevin Hilman <khilman@mvista.com>2007-03-08 14:32:19 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-04-21 16:02:55 -0400
commit075192ae807579448afcc0833bd349ccce057825 (patch)
tree5627ae9d65bc8791793fde1bc2ad8f62bc015e44 /arch/arm/plat-omap
parent89df127246f23add865f4a8f719c990e41151843 (diff)
[ARM] 4262/1: OMAP: clocksource and clockevent support
Update OMAP1 to enable support for hrtimers and dynticks by using new clocksource and clockevent infrastructure. Signed-off-by: Kevin Hilman <khilman@mvista.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/Kconfig1
-rw-r--r--arch/arm/plat-omap/common.c50
-rw-r--r--arch/arm/plat-omap/timer32k.c139
3 files changed, 90 insertions, 100 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index f2dc363de66b..9e8d21eca4ec 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -11,6 +11,7 @@ choice
11 11
12config ARCH_OMAP1 12config ARCH_OMAP1
13 bool "TI OMAP1" 13 bool "TI OMAP1"
14 select GENERIC_CLOCKEVENTS
14 15
15config ARCH_OMAP2 16config ARCH_OMAP2
16 bool "TI OMAP2" 17 bool "TI OMAP2"
diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c
index 57b7b93674a4..fecd3d625995 100644
--- a/arch/arm/plat-omap/common.c
+++ b/arch/arm/plat-omap/common.c
@@ -156,3 +156,53 @@ static int __init omap_add_serial_console(void)
156 return add_preferred_console("ttyS", line, opt); 156 return add_preferred_console("ttyS", line, opt);
157} 157}
158console_initcall(omap_add_serial_console); 158console_initcall(omap_add_serial_console);
159
160
161/*
162 * 32KHz clocksource ... always available, on pretty most chips except
163 * OMAP 730 and 1510. Other timers could be used as clocksources, with
164 * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
165 * but systems won't necessarily want to spend resources that way.
166 */
167
168#if defined(CONFIG_ARCH_OMAP16XX)
169#define TIMER_32K_SYNCHRONIZED 0xfffbc410
170#elif defined(CONFIG_ARCH_OMAP24XX)
171#define TIMER_32K_SYNCHRONIZED 0x48004010
172#endif
173
174#ifdef TIMER_32K_SYNCHRONIZED
175
176#include <linux/clocksource.h>
177
178static cycle_t omap_32k_read(void)
179{
180 return omap_readl(TIMER_32K_SYNCHRONIZED);
181}
182
183static struct clocksource clocksource_32k = {
184 .name = "32k_counter",
185 .rating = 250,
186 .read = omap_32k_read,
187 .mask = CLOCKSOURCE_MASK(32),
188 .shift = 10,
189 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
190};
191
192static int __init omap_init_clocksource_32k(void)
193{
194 static char err[] __initdata = KERN_ERR
195 "%s: can't register clocksource!\n";
196
197 if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
198 clocksource_32k.mult = clocksource_hz2mult(32768,
199 clocksource_32k.shift);
200
201 if (clocksource_register(&clocksource_32k))
202 printk(err, clocksource_32k.name);
203 }
204 return 0;
205}
206arch_initcall(omap_init_clocksource_32k);
207
208#endif /* TIMER_32K_SYNCHRONIZED */
diff --git a/arch/arm/plat-omap/timer32k.c b/arch/arm/plat-omap/timer32k.c
index 265310601161..114f87151d60 100644
--- a/arch/arm/plat-omap/timer32k.c
+++ b/arch/arm/plat-omap/timer32k.c
@@ -42,6 +42,8 @@
42#include <linux/spinlock.h> 42#include <linux/spinlock.h>
43#include <linux/err.h> 43#include <linux/err.h>
44#include <linux/clk.h> 44#include <linux/clk.h>
45#include <linux/clocksource.h>
46#include <linux/clockchips.h>
45 47
46#include <asm/system.h> 48#include <asm/system.h>
47#include <asm/hardware.h> 49#include <asm/hardware.h>
@@ -80,13 +82,13 @@ struct sys_timer omap_timer;
80#define OMAP1_32K_TIMER_TVR 0x00 82#define OMAP1_32K_TIMER_TVR 0x00
81#define OMAP1_32K_TIMER_TCR 0x04 83#define OMAP1_32K_TIMER_TCR 0x04
82 84
83#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) 85#define OMAP_32K_TICKS_PER_SEC (32768)
84 86
85/* 87/*
86 * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 88 * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1
87 * so with HZ = 128, TVR = 255. 89 * so with HZ = 128, TVR = 255.
88 */ 90 */
89#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) 91#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1)
90 92
91#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ 93#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \
92 (((nr_jiffies) * (clock_rate)) / HZ) 94 (((nr_jiffies) * (clock_rate)) / HZ)
@@ -142,6 +144,28 @@ static inline void omap_32k_timer_ack_irq(void)
142 144
143#endif 145#endif
144 146
147static void omap_32k_timer_set_mode(enum clock_event_mode mode,
148 struct clock_event_device *evt)
149{
150 switch (mode) {
151 case CLOCK_EVT_MODE_ONESHOT:
152 case CLOCK_EVT_MODE_PERIODIC:
153 omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
154 break;
155 case CLOCK_EVT_MODE_UNUSED:
156 case CLOCK_EVT_MODE_SHUTDOWN:
157 omap_32k_timer_stop();
158 break;
159 }
160}
161
162static struct clock_event_device clockevent_32k_timer = {
163 .name = "32k-timer",
164 .features = CLOCK_EVT_FEAT_PERIODIC,
165 .shift = 32,
166 .set_mode = omap_32k_timer_set_mode,
167};
168
145/* 169/*
146 * The 32KHz synchronized timer is an additional timer on 16xx. 170 * The 32KHz synchronized timer is an additional timer on 16xx.
147 * It is always running. 171 * It is always running.
@@ -171,15 +195,6 @@ omap_32k_ticks_to_nsecs(unsigned long ticks_32k)
171static unsigned long omap_32k_last_tick = 0; 195static unsigned long omap_32k_last_tick = 0;
172 196
173/* 197/*
174 * Returns elapsed usecs since last 32k timer interrupt
175 */
176static unsigned long omap_32k_timer_gettimeoffset(void)
177{
178 unsigned long now = omap_32k_sync_timer_read();
179 return omap_32k_ticks_to_usecs(now - omap_32k_last_tick);
180}
181
182/*
183 * Returns current time from boot in nsecs. It's OK for this to wrap 198 * Returns current time from boot in nsecs. It's OK for this to wrap
184 * around for now, as it's just a relative time stamp. 199 * around for now, as it's just a relative time stamp.
185 */ 200 */
@@ -188,95 +203,16 @@ unsigned long long sched_clock(void)
188 return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); 203 return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read());
189} 204}
190 205
191/*
192 * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this
193 * function is also called from other interrupts to remove latency
194 * issues with dynamic tick. In the dynamic tick case, we need to lock
195 * with irqsave.
196 */
197static inline irqreturn_t _omap_32k_timer_interrupt(int irq, void *dev_id)
198{
199 unsigned long now;
200
201 omap_32k_timer_ack_irq();
202 now = omap_32k_sync_timer_read();
203
204 while ((signed long)(now - omap_32k_last_tick)
205 >= OMAP_32K_TICKS_PER_HZ) {
206 omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ;
207 timer_tick();
208 }
209
210 /* Restart timer so we don't drift off due to modulo or dynamic tick.
211 * By default we program the next timer to be continuous to avoid
212 * latencies during high system load. During dynamic tick operation the
213 * continuous timer can be overridden from pm_idle to be longer.
214 */
215 omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now);
216
217 return IRQ_HANDLED;
218}
219
220static irqreturn_t omap_32k_timer_handler(int irq, void *dev_id)
221{
222 return _omap_32k_timer_interrupt(irq, dev_id);
223}
224
225static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) 206static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id)
226{ 207{
227 unsigned long flags; 208 struct clock_event_device *evt = &clockevent_32k_timer;
209 omap_32k_timer_ack_irq();
228 210
229 write_seqlock_irqsave(&xtime_lock, flags); 211 evt->event_handler(evt);
230 _omap_32k_timer_interrupt(irq, dev_id);
231 write_sequnlock_irqrestore(&xtime_lock, flags);
232 212
233 return IRQ_HANDLED; 213 return IRQ_HANDLED;
234} 214}
235 215
236#ifdef CONFIG_NO_IDLE_HZ
237/*
238 * Programs the next timer interrupt needed. Called when dynamic tick is
239 * enabled, and to reprogram the ticks to skip from pm_idle. Note that
240 * we can keep the timer continuous, and don't need to set it to run in
241 * one-shot mode. This is because the timer will get reprogrammed again
242 * after next interrupt.
243 */
244void omap_32k_timer_reprogram(unsigned long next_tick)
245{
246 unsigned long ticks = JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1;
247 unsigned long now = omap_32k_sync_timer_read();
248 unsigned long idled = now - omap_32k_last_tick;
249
250 if (idled + 1 < ticks)
251 ticks -= idled;
252 else
253 ticks = 1;
254 omap_32k_timer_start(ticks);
255}
256
257static struct irqaction omap_32k_timer_irq;
258extern struct timer_update_handler timer_update;
259
260static int omap_32k_timer_enable_dyn_tick(void)
261{
262 /* No need to reprogram timer, just use the next interrupt */
263 return 0;
264}
265
266static int omap_32k_timer_disable_dyn_tick(void)
267{
268 omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
269 return 0;
270}
271
272static struct dyn_tick_timer omap_dyn_tick_timer = {
273 .enable = omap_32k_timer_enable_dyn_tick,
274 .disable = omap_32k_timer_disable_dyn_tick,
275 .reprogram = omap_32k_timer_reprogram,
276 .handler = omap_32k_timer_handler,
277};
278#endif /* CONFIG_NO_IDLE_HZ */
279
280static struct irqaction omap_32k_timer_irq = { 216static struct irqaction omap_32k_timer_irq = {
281 .name = "32KHz timer", 217 .name = "32KHz timer",
282 .flags = IRQF_DISABLED | IRQF_TIMER, 218 .flags = IRQF_DISABLED | IRQF_TIMER,
@@ -285,13 +221,8 @@ static struct irqaction omap_32k_timer_irq = {
285 221
286static __init void omap_init_32k_timer(void) 222static __init void omap_init_32k_timer(void)
287{ 223{
288#ifdef CONFIG_NO_IDLE_HZ
289 omap_timer.dyn_tick = &omap_dyn_tick_timer;
290#endif
291
292 if (cpu_class_is_omap1()) 224 if (cpu_class_is_omap1())
293 setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); 225 setup_irq(INT_OS_TIMER, &omap_32k_timer_irq);
294 omap_timer.offset = omap_32k_timer_gettimeoffset;
295 omap_32k_last_tick = omap_32k_sync_timer_read(); 226 omap_32k_last_tick = omap_32k_sync_timer_read();
296 227
297#ifdef CONFIG_ARCH_OMAP2 228#ifdef CONFIG_ARCH_OMAP2
@@ -308,7 +239,16 @@ static __init void omap_init_32k_timer(void)
308 } 239 }
309#endif 240#endif
310 241
311 omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); 242 clockevent_32k_timer.mult = div_sc(OMAP_32K_TICKS_PER_SEC,
243 NSEC_PER_SEC,
244 clockevent_32k_timer.shift);
245 clockevent_32k_timer.max_delta_ns =
246 clockevent_delta2ns(0xfffffffe, &clockevent_32k_timer);
247 clockevent_32k_timer.min_delta_ns =
248 clockevent_delta2ns(1, &clockevent_32k_timer);
249
250 clockevent_32k_timer.cpumask = cpumask_of_cpu(0);
251 clockevents_register_device(&clockevent_32k_timer);
312} 252}
313 253
314/* 254/*
@@ -326,5 +266,4 @@ static void __init omap_timer_init(void)
326 266
327struct sys_timer omap_timer = { 267struct sys_timer omap_timer = {
328 .init = omap_timer_init, 268 .init = omap_timer_init,
329 .offset = NULL, /* Initialized later */
330}; 269};