mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 19:26:25 +00:00
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>
This commit is contained in:
parent
99ce567ba9
commit
19bdc9d061
1 changed files with 66 additions and 0 deletions
|
@ -47,6 +47,7 @@ struct sh_cmt_priv {
|
|||
unsigned long rate;
|
||||
spinlock_t lock;
|
||||
struct clock_event_device ced;
|
||||
struct clocksource cs;
|
||||
unsigned long total_cycles;
|
||||
};
|
||||
|
||||
|
@ -376,6 +377,68 @@ static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag)
|
|||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
}
|
||||
|
||||
static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs)
|
||||
{
|
||||
return container_of(cs, struct sh_cmt_priv, cs);
|
||||
}
|
||||
|
||||
static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
|
||||
{
|
||||
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
|
||||
unsigned long flags, raw;
|
||||
unsigned long value;
|
||||
int has_wrapped;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
value = p->total_cycles;
|
||||
raw = sh_cmt_get_counter(p, &has_wrapped);
|
||||
|
||||
if (unlikely(has_wrapped))
|
||||
raw = p->match_value;
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
return value + raw;
|
||||
}
|
||||
|
||||
static int sh_cmt_clocksource_enable(struct clocksource *cs)
|
||||
{
|
||||
struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
|
||||
int ret;
|
||||
|
||||
p->total_cycles = 0;
|
||||
|
||||
ret = sh_cmt_start(p, FLAG_CLOCKSOURCE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* TODO: calculate good shift from rate and counter bit width */
|
||||
cs->shift = 0;
|
||||
cs->mult = clocksource_hz2mult(p->rate, cs->shift);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_cmt_clocksource_disable(struct clocksource *cs)
|
||||
{
|
||||
sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
|
||||
}
|
||||
|
||||
static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
|
||||
char *name, unsigned long rating)
|
||||
{
|
||||
struct clocksource *cs = &p->cs;
|
||||
|
||||
cs->name = name;
|
||||
cs->rating = rating;
|
||||
cs->read = sh_cmt_clocksource_read;
|
||||
cs->enable = sh_cmt_clocksource_enable;
|
||||
cs->disable = sh_cmt_clocksource_disable;
|
||||
cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
|
||||
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
||||
pr_info("sh_cmt: %s used as clock source\n", cs->name);
|
||||
clocksource_register(cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced)
|
||||
{
|
||||
return container_of(ced, struct sh_cmt_priv, ced);
|
||||
|
@ -483,6 +546,9 @@ int sh_cmt_register(struct sh_cmt_priv *p, char *name,
|
|||
if (clockevent_rating)
|
||||
sh_cmt_register_clockevent(p, name, clockevent_rating);
|
||||
|
||||
if (clocksource_rating)
|
||||
sh_cmt_register_clocksource(p, name, clocksource_rating);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue