aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-omap/timer32k.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/timer32k.c')
-rw-r--r--arch/arm/plat-omap/timer32k.c139
1 files changed, 39 insertions, 100 deletions
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};