diff options
author | Tomasz Figa <tomasz.figa@gmail.com> | 2013-06-16 19:11:31 -0400 |
---|---|---|
committer | Tomasz Figa <tomasz.figa@gmail.com> | 2013-08-05 19:21:43 -0400 |
commit | 0b96258b420208ebaacc0ef4b21b67dba262badf (patch) | |
tree | 89bdeebfcbadacdfe614df5c998de38321f03163 | |
parent | 6792e636d5bfc1b26c25e7ed056b358e1144c6df (diff) |
clocksource: samsung_pwm_timer: Handle suspend/resume correctly
Current suspend/resume handling of the driver was broken, because:
- periodic timer was being enabled in CLOCK_EVT_MODE_RESUME mode, which
does not seem to be correct behavior looking at other platforms,
- PWM divisors need to be restored, but they were not,
- clockevent interrupt mask needs to be restored, but it was not,
- clocksource was being restored in clockevent resume callback.
This patch fixes issues mentioned above, making suspend/resume handling
in the driver correct.
Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Tested-by: Mark Brown <broonie@linaro.org>
Tested-by: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | drivers/clocksource/samsung_pwm_timer.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index b3112dc293ba..ac60f8b8a5f7 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c | |||
@@ -207,17 +207,6 @@ static int samsung_set_next_event(unsigned long cycles, | |||
207 | return 0; | 207 | return 0; |
208 | } | 208 | } |
209 | 209 | ||
210 | static void samsung_timer_resume(void) | ||
211 | { | ||
212 | /* event timer restart */ | ||
213 | samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1); | ||
214 | samsung_time_start(pwm.event_id, true); | ||
215 | |||
216 | /* source timer restart */ | ||
217 | samsung_time_setup(pwm.source_id, pwm.tcnt_max); | ||
218 | samsung_time_start(pwm.source_id, true); | ||
219 | } | ||
220 | |||
221 | static void samsung_set_mode(enum clock_event_mode mode, | 210 | static void samsung_set_mode(enum clock_event_mode mode, |
222 | struct clock_event_device *evt) | 211 | struct clock_event_device *evt) |
223 | { | 212 | { |
@@ -234,20 +223,29 @@ static void samsung_set_mode(enum clock_event_mode mode, | |||
234 | 223 | ||
235 | case CLOCK_EVT_MODE_UNUSED: | 224 | case CLOCK_EVT_MODE_UNUSED: |
236 | case CLOCK_EVT_MODE_SHUTDOWN: | 225 | case CLOCK_EVT_MODE_SHUTDOWN: |
237 | break; | ||
238 | |||
239 | case CLOCK_EVT_MODE_RESUME: | 226 | case CLOCK_EVT_MODE_RESUME: |
240 | samsung_timer_resume(); | ||
241 | break; | 227 | break; |
242 | } | 228 | } |
243 | } | 229 | } |
244 | 230 | ||
231 | static void samsung_clockevent_resume(struct clock_event_device *cev) | ||
232 | { | ||
233 | samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div); | ||
234 | samsung_timer_set_divisor(pwm.event_id, pwm.tdiv); | ||
235 | |||
236 | if (pwm.variant.has_tint_cstat) { | ||
237 | u32 mask = (1 << pwm.event_id); | ||
238 | writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); | ||
239 | } | ||
240 | } | ||
241 | |||
245 | static struct clock_event_device time_event_device = { | 242 | static struct clock_event_device time_event_device = { |
246 | .name = "samsung_event_timer", | 243 | .name = "samsung_event_timer", |
247 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 244 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
248 | .rating = 200, | 245 | .rating = 200, |
249 | .set_next_event = samsung_set_next_event, | 246 | .set_next_event = samsung_set_next_event, |
250 | .set_mode = samsung_set_mode, | 247 | .set_mode = samsung_set_mode, |
248 | .resume = samsung_clockevent_resume, | ||
251 | }; | 249 | }; |
252 | 250 | ||
253 | static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) | 251 | static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) |
@@ -298,6 +296,20 @@ static void __init samsung_clockevent_init(void) | |||
298 | } | 296 | } |
299 | } | 297 | } |
300 | 298 | ||
299 | static void samsung_clocksource_suspend(struct clocksource *cs) | ||
300 | { | ||
301 | samsung_time_stop(pwm.source_id); | ||
302 | } | ||
303 | |||
304 | static void samsung_clocksource_resume(struct clocksource *cs) | ||
305 | { | ||
306 | samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div); | ||
307 | samsung_timer_set_divisor(pwm.source_id, pwm.tdiv); | ||
308 | |||
309 | samsung_time_setup(pwm.source_id, pwm.tcnt_max); | ||
310 | samsung_time_start(pwm.source_id, true); | ||
311 | } | ||
312 | |||
301 | static cycle_t samsung_clocksource_read(struct clocksource *c) | 313 | static cycle_t samsung_clocksource_read(struct clocksource *c) |
302 | { | 314 | { |
303 | return ~readl_relaxed(pwm.source_reg); | 315 | return ~readl_relaxed(pwm.source_reg); |
@@ -307,6 +319,8 @@ static struct clocksource samsung_clocksource = { | |||
307 | .name = "samsung_clocksource_timer", | 319 | .name = "samsung_clocksource_timer", |
308 | .rating = 250, | 320 | .rating = 250, |
309 | .read = samsung_clocksource_read, | 321 | .read = samsung_clocksource_read, |
322 | .suspend = samsung_clocksource_suspend, | ||
323 | .resume = samsung_clocksource_resume, | ||
310 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 324 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
311 | }; | 325 | }; |
312 | 326 | ||