aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-pxa
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2007-11-08 18:35:46 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-11-08 18:35:46 -0500
commit91bc51d8a10b00d8233dd5b6f07d7eb40828b87d (patch)
tree35acc012af09a987364f39d0a5ccda7c4be35a3e /arch/arm/mach-pxa
parentc2ec21c5c8f15c079c209f403d582f3134785060 (diff)
[ARM] pxa: fix one-shot timer mode
One-shot timer mode on PXA has various bugs which prevent kernels build with NO_HZ enabled booting. They end up spinning on a permanently asserted timer interrupt because we don't properly clear it down - clearing the OIER bit does not stop the pending interrupt status. Fix this in the set_mode handler as well. Moreover, the code which sets the next expiry point may race with the hardware, and we might not set the match register sufficiently in the future. If we encounter that situation, return -ETIME so the generic time code retries. Acked-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-pxa')
-rw-r--r--arch/arm/mach-pxa/time.c23
1 files changed, 14 insertions, 9 deletions
diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c
index a2d45d742ce4..fbfa1920353d 100644
--- a/arch/arm/mach-pxa/time.c
+++ b/arch/arm/mach-pxa/time.c
@@ -68,6 +68,7 @@ pxa_ost0_interrupt(int irq, void *dev_id)
68 if (c->mode == CLOCK_EVT_MODE_ONESHOT) { 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 c->event_handler(c); 72 c->event_handler(c);
72 } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) { 73 } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) {
73 /* Call the event handler as many times as necessary 74 /* Call the event handler as many times as necessary
@@ -100,9 +101,9 @@ pxa_ost0_interrupt(int irq, void *dev_id)
100 * anything that might put us "very close". 101 * anything that might put us "very close".
101 */ 102 */
102#define MIN_OSCR_DELTA 16 103#define MIN_OSCR_DELTA 16
103 do { 104 do {
104 OSSR = OSSR_M0; 105 OSSR = OSSR_M0;
105 next_match = (OSMR0 += LATCH); 106 next_match = (OSMR0 += LATCH);
106 c->event_handler(c); 107 c->event_handler(c);
107 } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA) 108 } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA)
108 && (c->mode == CLOCK_EVT_MODE_PERIODIC)); 109 && (c->mode == CLOCK_EVT_MODE_PERIODIC));
@@ -114,14 +115,16 @@ pxa_ost0_interrupt(int irq, void *dev_id)
114static int 115static int
115pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev) 116pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
116{ 117{
117 unsigned long irqflags; 118 unsigned long flags, next, oscr;
118 119
119 raw_local_irq_save(irqflags); 120 raw_local_irq_save(flags);
120 OSMR0 = OSCR + delta;
121 OSSR = OSSR_M0;
122 OIER |= OIER_E0; 121 OIER |= OIER_E0;
123 raw_local_irq_restore(irqflags); 122 next = OSCR + delta;
124 return 0; 123 OSMR0 = next;
124 oscr = OSCR;
125 raw_local_irq_restore(flags);
126
127 return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
125} 128}
126 129
127static void 130static void
@@ -132,15 +135,16 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
132 switch (mode) { 135 switch (mode) {
133 case CLOCK_EVT_MODE_PERIODIC: 136 case CLOCK_EVT_MODE_PERIODIC:
134 raw_local_irq_save(irqflags); 137 raw_local_irq_save(irqflags);
135 OSMR0 = OSCR + LATCH;
136 OSSR = OSSR_M0; 138 OSSR = OSSR_M0;
137 OIER |= OIER_E0; 139 OIER |= OIER_E0;
140 OSMR0 = OSCR + LATCH;
138 raw_local_irq_restore(irqflags); 141 raw_local_irq_restore(irqflags);
139 break; 142 break;
140 143
141 case CLOCK_EVT_MODE_ONESHOT: 144 case CLOCK_EVT_MODE_ONESHOT:
142 raw_local_irq_save(irqflags); 145 raw_local_irq_save(irqflags);
143 OIER &= ~OIER_E0; 146 OIER &= ~OIER_E0;
147 OSSR = OSSR_M0;
144 raw_local_irq_restore(irqflags); 148 raw_local_irq_restore(irqflags);
145 break; 149 break;
146 150
@@ -149,6 +153,7 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
149 /* initializing, released, or preparing for suspend */ 153 /* initializing, released, or preparing for suspend */
150 raw_local_irq_save(irqflags); 154 raw_local_irq_save(irqflags);
151 OIER &= ~OIER_E0; 155 OIER &= ~OIER_E0;
156 OSSR = OSSR_M0;
152 raw_local_irq_restore(irqflags); 157 raw_local_irq_restore(irqflags);
153 break; 158 break;
154 159