clocksource: TCLIB: Allow higher clock rates for clock events
As default the TCLIB uses the 32KiHz base clock rate for clock events. Add a compile time selection to allow higher clock resulution. (fixed up by Sami Pietikäinen <Sami.Pietikainen@wapice.com>) Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
d4342a334f
commit
faf1d77f93
|
@ -23,8 +23,7 @@
|
|||
* this 32 bit free-running counter. the second channel is not used.
|
||||
*
|
||||
* - The third channel may be used to provide a 16-bit clockevent
|
||||
* source, used in either periodic or oneshot mode. This runs
|
||||
* at 32 KiHZ, and can handle delays of up to two seconds.
|
||||
* source, used in either periodic or oneshot mode.
|
||||
*
|
||||
* A boot clocksource and clockevent source are also currently needed,
|
||||
* unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so
|
||||
|
@ -74,6 +73,7 @@ static struct clocksource clksrc = {
|
|||
struct tc_clkevt_device {
|
||||
struct clock_event_device clkevt;
|
||||
struct clk *clk;
|
||||
u32 freq;
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
|
@ -82,13 +82,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
|
|||
return container_of(clkevt, struct tc_clkevt_device, clkevt);
|
||||
}
|
||||
|
||||
/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
|
||||
* because using one of the divided clocks would usually mean the
|
||||
* tick rate can never be less than several dozen Hz (vs 0.5 Hz).
|
||||
*
|
||||
* A divided clock could be good for high resolution timers, since
|
||||
* 30.5 usec resolution can seem "low".
|
||||
*/
|
||||
static u32 timer_clock;
|
||||
|
||||
static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
|
||||
|
@ -111,11 +104,12 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
|
|||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
clk_prepare_enable(tcd->clk);
|
||||
|
||||
/* slow clock, count up to RC, then irq and restart */
|
||||
/* count up to RC, then irq and restart */
|
||||
__raw_writel(timer_clock
|
||||
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
|
||||
regs + ATMEL_TC_REG(2, CMR));
|
||||
__raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
|
||||
__raw_writel((tcd->freq + HZ/2)/HZ,
|
||||
tcaddr + ATMEL_TC_REG(2, RC));
|
||||
|
||||
/* Enable clock and interrupts on RC compare */
|
||||
__raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
|
||||
|
@ -128,7 +122,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
|
|||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
clk_prepare_enable(tcd->clk);
|
||||
|
||||
/* slow clock, count up to RC, then irq and stop */
|
||||
/* count up to RC, then irq and stop */
|
||||
__raw_writel(timer_clock | ATMEL_TC_CPCSTOP
|
||||
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
|
||||
regs + ATMEL_TC_REG(2, CMR));
|
||||
|
@ -157,8 +151,12 @@ static struct tc_clkevt_device clkevt = {
|
|||
.name = "tc_clkevt",
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC
|
||||
| CLOCK_EVT_FEAT_ONESHOT,
|
||||
#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
|
||||
/* Should be lower than at91rm9200's system timer */
|
||||
.rating = 125,
|
||||
#else
|
||||
.rating = 200,
|
||||
#endif
|
||||
.set_next_event = tc_next_event,
|
||||
.set_mode = tc_mode,
|
||||
},
|
||||
|
@ -184,8 +182,9 @@ static struct irqaction tc_irqaction = {
|
|||
.handler = ch2_irq,
|
||||
};
|
||||
|
||||
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
|
||||
static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
|
||||
{
|
||||
unsigned divisor = atmel_tc_divisors[divisor_idx];
|
||||
int ret;
|
||||
struct clk *t2_clk = tc->clk[2];
|
||||
int irq = tc->irq[2];
|
||||
|
@ -200,7 +199,11 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
|
|||
clkevt.clk = t2_clk;
|
||||
tc_irqaction.dev_id = &clkevt;
|
||||
|
||||
timer_clock = clk32k_divisor_idx;
|
||||
timer_clock = divisor_idx;
|
||||
if (!divisor)
|
||||
clkevt.freq = 32768;
|
||||
else
|
||||
clkevt.freq = clk_get_rate(t2_clk) / divisor;
|
||||
|
||||
clkevt.clkevt.cpumask = cpumask_of(0);
|
||||
|
||||
|
@ -208,7 +211,7 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
|
||||
clockevents_config_and_register(&clkevt.clkevt, clkevt.freq, 1, 0xffff);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -345,7 +348,11 @@ static int __init tcb_clksrc_init(void)
|
|||
goto err_disable_t1;
|
||||
|
||||
/* channel 2: periodic and oneshot timer support */
|
||||
#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
|
||||
ret = setup_clkevents(tc, clk32k_divisor_idx);
|
||||
#else
|
||||
ret = setup_clkevents(tc, best_divisor_idx);
|
||||
#endif
|
||||
if (ret)
|
||||
goto err_unregister_clksrc;
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@ config ATMEL_TCB_CLKSRC
|
|||
are combined to make a single 32-bit timer.
|
||||
|
||||
When GENERIC_CLOCKEVENTS is defined, the third timer channel
|
||||
may be used as a clock event device supporting oneshot mode
|
||||
(delays of up to two seconds) based on the 32 KiHz clock.
|
||||
may be used as a clock event device supporting oneshot mode.
|
||||
|
||||
config ATMEL_TCB_CLKSRC_BLOCK
|
||||
int
|
||||
|
@ -93,6 +92,15 @@ config ATMEL_TCB_CLKSRC_BLOCK
|
|||
TC can be used for other purposes, such as PWM generation and
|
||||
interval timing.
|
||||
|
||||
config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
|
||||
bool "TC Block use 32 KiHz clock"
|
||||
depends on ATMEL_TCB_CLKSRC
|
||||
default y
|
||||
help
|
||||
Select this to use 32 KiHz base clock rate as TC block clock
|
||||
source for clock events.
|
||||
|
||||
|
||||
config DUMMY_IRQ
|
||||
tristate "Dummy IRQ handler"
|
||||
default n
|
||||
|
|
Loading…
Reference in New Issue