| From: Benedikt Spranger <b.spranger@linutronix.de> |
| Date: Mon, 8 Mar 2010 18:57:04 +0100 |
| Subject: 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> |
| --- |
| drivers/clocksource/tcb_clksrc.c | 36 +++++++++++++++++++++--------------- |
| drivers/misc/Kconfig | 12 ++++++++++-- |
| 2 files changed, 31 insertions(+), 17 deletions(-) |
| |
| --- a/drivers/clocksource/tcb_clksrc.c |
| +++ b/drivers/clocksource/tcb_clksrc.c |
| @@ -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 |
| @@ -75,6 +74,7 @@ struct tc_clkevt_device { |
| struct clock_event_device clkevt; |
| struct clk *clk; |
| bool clk_enabled; |
| + u32 freq; |
| void __iomem *regs; |
| }; |
| |
| @@ -83,13 +83,6 @@ static struct tc_clkevt_device *to_tc_cl |
| 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_clk_disable(struct clock_event_device *d) |
| @@ -139,7 +132,7 @@ static int tc_set_oneshot(struct clock_e |
| |
| tc_clk_enable(d); |
| |
| - /* 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)); |
| __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
| @@ -161,10 +154,10 @@ static int tc_set_periodic(struct clock_ |
| */ |
| tc_clk_enable(d); |
| |
| - /* 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)); |
| @@ -191,7 +184,11 @@ static struct tc_clkevt_device clkevt = |
| .features = CLOCK_EVT_FEAT_PERIODIC | |
| CLOCK_EVT_FEAT_ONESHOT, |
| /* Should be lower than at91rm9200's system timer */ |
| +#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK |
| .rating = 125, |
| +#else |
| + .rating = 200, |
| +#endif |
| .set_next_event = tc_next_event, |
| .set_state_shutdown = tc_shutdown_clk_off, |
| .set_state_periodic = tc_set_periodic, |
| @@ -213,8 +210,9 @@ static irqreturn_t ch2_irq(int irq, void |
| return IRQ_NONE; |
| } |
| |
| -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]; |
| @@ -235,7 +233,11 @@ static int __init setup_clkevents(struct |
| clkevt.regs = tc->regs; |
| clkevt.clk = t2_clk; |
| |
| - 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); |
| |
| @@ -246,7 +248,7 @@ static int __init setup_clkevents(struct |
| return ret; |
| } |
| |
| - clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); |
| + clockevents_config_and_register(&clkevt.clkevt, clkevt.freq, 1, 0xffff); |
| |
| return ret; |
| } |
| @@ -383,7 +385,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; |
| |
| --- a/drivers/misc/Kconfig |
| +++ b/drivers/misc/Kconfig |
| @@ -69,8 +69,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 |
| @@ -84,6 +83,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 |