diff options
Diffstat (limited to 'arch/arm/mach-pxa/time.c')
-rw-r--r-- | arch/arm/mach-pxa/time.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c new file mode 100644 index 000000000000..473fb6173f72 --- /dev/null +++ b/arch/arm/mach-pxa/time.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-pxa/time.c | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Created: Jun 15, 2001 | ||
6 | * Copyright: MontaVista Software Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/signal.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/sched.h> | ||
22 | |||
23 | #include <asm/system.h> | ||
24 | #include <asm/hardware.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/leds.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/mach/irq.h> | ||
29 | #include <asm/mach/time.h> | ||
30 | #include <asm/arch/pxa-regs.h> | ||
31 | |||
32 | |||
33 | static inline unsigned long pxa_get_rtc_time(void) | ||
34 | { | ||
35 | return RCNR; | ||
36 | } | ||
37 | |||
38 | static int pxa_set_rtc(void) | ||
39 | { | ||
40 | unsigned long current_time = xtime.tv_sec; | ||
41 | |||
42 | if (RTSR & RTSR_ALE) { | ||
43 | /* make sure not to forward the clock over an alarm */ | ||
44 | unsigned long alarm = RTAR; | ||
45 | if (current_time >= alarm && alarm >= RCNR) | ||
46 | return -ERESTARTSYS; | ||
47 | } | ||
48 | RCNR = current_time; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /* IRQs are disabled before entering here from do_gettimeofday() */ | ||
53 | static unsigned long pxa_gettimeoffset (void) | ||
54 | { | ||
55 | long ticks_to_match, elapsed, usec; | ||
56 | |||
57 | /* Get ticks before next timer match */ | ||
58 | ticks_to_match = OSMR0 - OSCR; | ||
59 | |||
60 | /* We need elapsed ticks since last match */ | ||
61 | elapsed = LATCH - ticks_to_match; | ||
62 | |||
63 | /* don't get fooled by the workaround in pxa_timer_interrupt() */ | ||
64 | if (elapsed <= 0) | ||
65 | return 0; | ||
66 | |||
67 | /* Now convert them to usec */ | ||
68 | usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; | ||
69 | |||
70 | return usec; | ||
71 | } | ||
72 | |||
73 | static irqreturn_t | ||
74 | pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
75 | { | ||
76 | int next_match; | ||
77 | |||
78 | write_seqlock(&xtime_lock); | ||
79 | |||
80 | /* Loop until we get ahead of the free running timer. | ||
81 | * This ensures an exact clock tick count and time accuracy. | ||
82 | * IRQs are disabled inside the loop to ensure coherence between | ||
83 | * lost_ticks (updated in do_timer()) and the match reg value, so we | ||
84 | * can use do_gettimeofday() from interrupt handlers. | ||
85 | * | ||
86 | * HACK ALERT: it seems that the PXA timer regs aren't updated right | ||
87 | * away in all cases when a write occurs. We therefore compare with | ||
88 | * 8 instead of 0 in the while() condition below to avoid missing a | ||
89 | * match if OSCR has already reached the next OSMR value. | ||
90 | * Experience has shown that up to 6 ticks are needed to work around | ||
91 | * this problem, but let's use 8 to be conservative. Note that this | ||
92 | * affect things only when the timer IRQ has been delayed by nearly | ||
93 | * exactly one tick period which should be a pretty rare event. | ||
94 | */ | ||
95 | do { | ||
96 | timer_tick(regs); | ||
97 | OSSR = OSSR_M0; /* Clear match on timer 0 */ | ||
98 | next_match = (OSMR0 += LATCH); | ||
99 | } while( (signed long)(next_match - OSCR) <= 8 ); | ||
100 | |||
101 | write_sequnlock(&xtime_lock); | ||
102 | |||
103 | return IRQ_HANDLED; | ||
104 | } | ||
105 | |||
106 | static struct irqaction pxa_timer_irq = { | ||
107 | .name = "PXA Timer Tick", | ||
108 | .flags = SA_INTERRUPT, | ||
109 | .handler = pxa_timer_interrupt | ||
110 | }; | ||
111 | |||
112 | static void __init pxa_timer_init(void) | ||
113 | { | ||
114 | struct timespec tv; | ||
115 | |||
116 | set_rtc = pxa_set_rtc; | ||
117 | |||
118 | tv.tv_nsec = 0; | ||
119 | tv.tv_sec = pxa_get_rtc_time(); | ||
120 | do_settimeofday(&tv); | ||
121 | |||
122 | OSMR0 = 0; /* set initial match at 0 */ | ||
123 | OSSR = 0xf; /* clear status on all timers */ | ||
124 | setup_irq(IRQ_OST0, &pxa_timer_irq); | ||
125 | OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */ | ||
126 | OSCR = 0; /* initialize free-running timer, force first match */ | ||
127 | } | ||
128 | |||
129 | #ifdef CONFIG_PM | ||
130 | static unsigned long osmr[4], oier; | ||
131 | |||
132 | static void pxa_timer_suspend(void) | ||
133 | { | ||
134 | osmr[0] = OSMR0; | ||
135 | osmr[1] = OSMR1; | ||
136 | osmr[2] = OSMR2; | ||
137 | osmr[3] = OSMR3; | ||
138 | oier = OIER; | ||
139 | } | ||
140 | |||
141 | static void pxa_timer_resume(void) | ||
142 | { | ||
143 | OSMR0 = osmr[0]; | ||
144 | OSMR1 = osmr[1]; | ||
145 | OSMR2 = osmr[2]; | ||
146 | OSMR3 = osmr[3]; | ||
147 | OIER = oier; | ||
148 | |||
149 | /* | ||
150 | * OSMR0 is the system timer: make sure OSCR is sufficiently behind | ||
151 | */ | ||
152 | OSCR = OSMR0 - LATCH; | ||
153 | } | ||
154 | #else | ||
155 | #define pxa_timer_suspend NULL | ||
156 | #define pxa_timer_resume NULL | ||
157 | #endif | ||
158 | |||
159 | struct sys_timer pxa_timer = { | ||
160 | .init = pxa_timer_init, | ||
161 | .suspend = pxa_timer_suspend, | ||
162 | .resume = pxa_timer_resume, | ||
163 | .offset = pxa_gettimeoffset, | ||
164 | }; | ||