diff options
Diffstat (limited to 'arch/arm/mach-pxa/time.c')
-rw-r--r-- | arch/arm/mach-pxa/time.c | 84 |
1 files changed, 21 insertions, 63 deletions
diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index fbfa1920353d..7b7c0179795b 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c | |||
@@ -59,55 +59,17 @@ unsigned long long sched_clock(void) | |||
59 | } | 59 | } |
60 | 60 | ||
61 | 61 | ||
62 | #define MIN_OSCR_DELTA 16 | ||
63 | |||
62 | static irqreturn_t | 64 | static irqreturn_t |
63 | pxa_ost0_interrupt(int irq, void *dev_id) | 65 | pxa_ost0_interrupt(int irq, void *dev_id) |
64 | { | 66 | { |
65 | int next_match; | ||
66 | struct clock_event_device *c = dev_id; | 67 | struct clock_event_device *c = dev_id; |
67 | 68 | ||
68 | if (c->mode == CLOCK_EVT_MODE_ONESHOT) { | 69 | /* Disarm the compare/match, signal the event. */ |
69 | /* Disarm the compare/match, signal the event. */ | 70 | OIER &= ~OIER_E0; |
70 | OIER &= ~OIER_E0; | 71 | OSSR = OSSR_M0; |
71 | OSSR = OSSR_M0; | 72 | c->event_handler(c); |
72 | c->event_handler(c); | ||
73 | } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) { | ||
74 | /* Call the event handler as many times as necessary | ||
75 | * to recover missed events, if any (if we update | ||
76 | * OSMR0 and OSCR0 is still ahead of us, we've missed | ||
77 | * the event). As we're dealing with that, re-arm the | ||
78 | * compare/match for the next event. | ||
79 | * | ||
80 | * HACK ALERT: | ||
81 | * | ||
82 | * There's a latency between the instruction that | ||
83 | * writes to OSMR0 and the actual commit to the | ||
84 | * physical hardware, because the CPU doesn't (have | ||
85 | * to) run at bus speed, there's a write buffer | ||
86 | * between the CPU and the bus, etc. etc. So if the | ||
87 | * target OSCR0 is "very close", to the OSMR0 load | ||
88 | * value, the update to OSMR0 might not get to the | ||
89 | * hardware in time and we'll miss that interrupt. | ||
90 | * | ||
91 | * To be safe, if the new OSMR0 is "very close" to the | ||
92 | * target OSCR0 value, we call the event_handler as | ||
93 | * though the event actually happened. According to | ||
94 | * Nico's comment in the previous version of this | ||
95 | * code, experience has shown that 6 OSCR ticks is | ||
96 | * "very close" but he went with 8. We will use 16, | ||
97 | * based on the results of testing on PXA270. | ||
98 | * | ||
99 | * To be doubly sure, we also tell clkevt via | ||
100 | * clockevents_register_device() not to ask for | ||
101 | * anything that might put us "very close". | ||
102 | */ | ||
103 | #define MIN_OSCR_DELTA 16 | ||
104 | do { | ||
105 | OSSR = OSSR_M0; | ||
106 | next_match = (OSMR0 += LATCH); | ||
107 | c->event_handler(c); | ||
108 | } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA) | ||
109 | && (c->mode == CLOCK_EVT_MODE_PERIODIC)); | ||
110 | } | ||
111 | 73 | ||
112 | return IRQ_HANDLED; | 74 | return IRQ_HANDLED; |
113 | } | 75 | } |
@@ -133,14 +95,6 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) | |||
133 | unsigned long irqflags; | 95 | unsigned long irqflags; |
134 | 96 | ||
135 | switch (mode) { | 97 | switch (mode) { |
136 | case CLOCK_EVT_MODE_PERIODIC: | ||
137 | raw_local_irq_save(irqflags); | ||
138 | OSSR = OSSR_M0; | ||
139 | OIER |= OIER_E0; | ||
140 | OSMR0 = OSCR + LATCH; | ||
141 | raw_local_irq_restore(irqflags); | ||
142 | break; | ||
143 | |||
144 | case CLOCK_EVT_MODE_ONESHOT: | 98 | case CLOCK_EVT_MODE_ONESHOT: |
145 | raw_local_irq_save(irqflags); | 99 | raw_local_irq_save(irqflags); |
146 | OIER &= ~OIER_E0; | 100 | OIER &= ~OIER_E0; |
@@ -158,13 +112,14 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) | |||
158 | break; | 112 | break; |
159 | 113 | ||
160 | case CLOCK_EVT_MODE_RESUME: | 114 | case CLOCK_EVT_MODE_RESUME: |
115 | case CLOCK_EVT_MODE_PERIODIC: | ||
161 | break; | 116 | break; |
162 | } | 117 | } |
163 | } | 118 | } |
164 | 119 | ||
165 | static struct clock_event_device ckevt_pxa_osmr0 = { | 120 | static struct clock_event_device ckevt_pxa_osmr0 = { |
166 | .name = "osmr0", | 121 | .name = "osmr0", |
167 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 122 | .features = CLOCK_EVT_FEAT_ONESHOT, |
168 | .shift = 32, | 123 | .shift = 32, |
169 | .rating = 200, | 124 | .rating = 200, |
170 | .cpumask = CPU_MASK_CPU0, | 125 | .cpumask = CPU_MASK_CPU0, |
@@ -214,7 +169,7 @@ static void __init pxa_timer_init(void) | |||
214 | ckevt_pxa_osmr0.max_delta_ns = | 169 | ckevt_pxa_osmr0.max_delta_ns = |
215 | clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0); | 170 | clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0); |
216 | ckevt_pxa_osmr0.min_delta_ns = | 171 | ckevt_pxa_osmr0.min_delta_ns = |
217 | clockevent_delta2ns(MIN_OSCR_DELTA, &ckevt_pxa_osmr0) + 1; | 172 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1; |
218 | 173 | ||
219 | cksrc_pxa_oscr0.mult = | 174 | cksrc_pxa_oscr0.mult = |
220 | clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift); | 175 | clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift); |
@@ -226,7 +181,7 @@ static void __init pxa_timer_init(void) | |||
226 | } | 181 | } |
227 | 182 | ||
228 | #ifdef CONFIG_PM | 183 | #ifdef CONFIG_PM |
229 | static unsigned long osmr[4], oier; | 184 | static unsigned long osmr[4], oier, oscr; |
230 | 185 | ||
231 | static void pxa_timer_suspend(void) | 186 | static void pxa_timer_suspend(void) |
232 | { | 187 | { |
@@ -235,23 +190,26 @@ static void pxa_timer_suspend(void) | |||
235 | osmr[2] = OSMR2; | 190 | osmr[2] = OSMR2; |
236 | osmr[3] = OSMR3; | 191 | osmr[3] = OSMR3; |
237 | oier = OIER; | 192 | oier = OIER; |
193 | oscr = OSCR; | ||
238 | } | 194 | } |
239 | 195 | ||
240 | static void pxa_timer_resume(void) | 196 | static void pxa_timer_resume(void) |
241 | { | 197 | { |
198 | /* | ||
199 | * Ensure that we have at least MIN_OSCR_DELTA between match | ||
200 | * register 0 and the OSCR, to guarantee that we will receive | ||
201 | * the one-shot timer interrupt. We adjust OSMR0 in preference | ||
202 | * to OSCR to guarantee that OSCR is monotonically incrementing. | ||
203 | */ | ||
204 | if (osmr[0] - oscr < MIN_OSCR_DELTA) | ||
205 | osmr[0] += MIN_OSCR_DELTA; | ||
206 | |||
242 | OSMR0 = osmr[0]; | 207 | OSMR0 = osmr[0]; |
243 | OSMR1 = osmr[1]; | 208 | OSMR1 = osmr[1]; |
244 | OSMR2 = osmr[2]; | 209 | OSMR2 = osmr[2]; |
245 | OSMR3 = osmr[3]; | 210 | OSMR3 = osmr[3]; |
246 | OIER = oier; | 211 | OIER = oier; |
247 | 212 | OSCR = oscr; | |
248 | /* | ||
249 | * OSCR0 is the system timer, which has to increase | ||
250 | * monotonically until it rolls over in hardware. The value | ||
251 | * (OSMR0 - LATCH) is OSCR0 at the most recent system tick, | ||
252 | * which is a handy value to restore to OSCR0. | ||
253 | */ | ||
254 | OSCR = OSMR0 - LATCH; | ||
255 | } | 213 | } |
256 | #else | 214 | #else |
257 | #define pxa_timer_suspend NULL | 215 | #define pxa_timer_suspend NULL |