diff options
author | Magnus Damm <damm@igel.co.jp> | 2009-04-17 01:26:31 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-04-21 20:31:04 -0400 |
commit | 19bdc9d061bcb71efd2b53083d96b59bbe1a1751 (patch) | |
tree | ebe6328a1078cd8526d9635ce54057b18984f30f | |
parent | 99ce567ba912109c78762246c964327f3f81f27d (diff) |
clocksource: sh_cmt clocksource support
Add clocksource support to the sh_cmt driver. With this in
place we can do tickless with a single CMT channel.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r-- | drivers/clocksource/sh_cmt.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 02bae3994abe..c24756489612 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c | |||
@@ -47,6 +47,7 @@ struct sh_cmt_priv { | |||
47 | unsigned long rate; | 47 | unsigned long rate; |
48 | spinlock_t lock; | 48 | spinlock_t lock; |
49 | struct clock_event_device ced; | 49 | struct clock_event_device ced; |
50 | struct clocksource cs; | ||
50 | unsigned long total_cycles; | 51 | unsigned long total_cycles; |
51 | }; | 52 | }; |
52 | 53 | ||
@@ -376,6 +377,68 @@ static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag) | |||
376 | spin_unlock_irqrestore(&p->lock, flags); | 377 | spin_unlock_irqrestore(&p->lock, flags); |
377 | } | 378 | } |
378 | 379 | ||
380 | static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs) | ||
381 | { | ||
382 | return container_of(cs, struct sh_cmt_priv, cs); | ||
383 | } | ||
384 | |||
385 | static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) | ||
386 | { | ||
387 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); | ||
388 | unsigned long flags, raw; | ||
389 | unsigned long value; | ||
390 | int has_wrapped; | ||
391 | |||
392 | spin_lock_irqsave(&p->lock, flags); | ||
393 | value = p->total_cycles; | ||
394 | raw = sh_cmt_get_counter(p, &has_wrapped); | ||
395 | |||
396 | if (unlikely(has_wrapped)) | ||
397 | raw = p->match_value; | ||
398 | spin_unlock_irqrestore(&p->lock, flags); | ||
399 | |||
400 | return value + raw; | ||
401 | } | ||
402 | |||
403 | static int sh_cmt_clocksource_enable(struct clocksource *cs) | ||
404 | { | ||
405 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); | ||
406 | int ret; | ||
407 | |||
408 | p->total_cycles = 0; | ||
409 | |||
410 | ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); | ||
411 | if (ret) | ||
412 | return ret; | ||
413 | |||
414 | /* TODO: calculate good shift from rate and counter bit width */ | ||
415 | cs->shift = 0; | ||
416 | cs->mult = clocksource_hz2mult(p->rate, cs->shift); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static void sh_cmt_clocksource_disable(struct clocksource *cs) | ||
421 | { | ||
422 | sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); | ||
423 | } | ||
424 | |||
425 | static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, | ||
426 | char *name, unsigned long rating) | ||
427 | { | ||
428 | struct clocksource *cs = &p->cs; | ||
429 | |||
430 | cs->name = name; | ||
431 | cs->rating = rating; | ||
432 | cs->read = sh_cmt_clocksource_read; | ||
433 | cs->enable = sh_cmt_clocksource_enable; | ||
434 | cs->disable = sh_cmt_clocksource_disable; | ||
435 | cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); | ||
436 | cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; | ||
437 | pr_info("sh_cmt: %s used as clock source\n", cs->name); | ||
438 | clocksource_register(cs); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
379 | static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced) | 442 | static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced) |
380 | { | 443 | { |
381 | return container_of(ced, struct sh_cmt_priv, ced); | 444 | return container_of(ced, struct sh_cmt_priv, ced); |
@@ -483,6 +546,9 @@ int sh_cmt_register(struct sh_cmt_priv *p, char *name, | |||
483 | if (clockevent_rating) | 546 | if (clockevent_rating) |
484 | sh_cmt_register_clockevent(p, name, clockevent_rating); | 547 | sh_cmt_register_clockevent(p, name, clockevent_rating); |
485 | 548 | ||
549 | if (clocksource_rating) | ||
550 | sh_cmt_register_clocksource(p, name, clocksource_rating); | ||
551 | |||
486 | return 0; | 552 | return 0; |
487 | } | 553 | } |
488 | 554 | ||