From 9a0dc7c04ee604fa928483a3378c501e1af61189 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Thu, 28 Jun 2012 11:17:55 +0800 Subject: [PATCH] rk30: timer: try fix asynchronous read timer, return value may undefined --- arch/arm/mach-rk30/clock_data.c | 2 +- arch/arm/mach-rk30/timer.c | 58 ++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-rk30/clock_data.c b/arch/arm/mach-rk30/clock_data.c index 1f24fcb58309..3ef0cdfad4f2 100644 --- a/arch/arm/mach-rk30/clock_data.c +++ b/arch/arm/mach-rk30/clock_data.c @@ -3027,7 +3027,7 @@ static void __init rk30_init_enable_clocks(void) #endif - #if 1 + #if 0 clk_enable_nolock(&clk_timer0); clk_enable_nolock(&clk_pclk_timer0); diff --git a/arch/arm/mach-rk30/timer.c b/arch/arm/mach-rk30/timer.c index 23ab6ef39a35..87addf2b11b8 100755 --- a/arch/arm/mach-rk30/timer.c +++ b/arch/arm/mach-rk30/timer.c @@ -70,28 +70,41 @@ static inline u32 timer_read(u32 n, u32 offset) #define IRQ_NR_TIMER_CLKSRC IRQ_TIMER1 #define TIMER_CLKSRC_NAME "timer1" +static inline u32 rk30_timer_read_current_value(u32 n) +{ + unsigned long flags; + u32 v[3]; + int loop = 100; + + do { + local_irq_save(flags); + v[0] = RK_TIMER_READVALUE(n); + v[1] = RK_TIMER_READVALUE(n); + v[2] = RK_TIMER_READVALUE(n); + local_irq_restore(flags); + if ((v[0] >= v[1]) && ((v[0] - v[1]) < 24) + && (v[1] >= v[2]) && ((v[1] - v[2]) < 24)) + break; + } while (loop--); + + return v[1]; +} + static int rk30_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { do { RK_TIMER_DISABLE(TIMER_CLKEVT); RK_TIMER_SETCOUNT(TIMER_CLKEVT, cycles); RK_TIMER_ENABLE(TIMER_CLKEVT); - } while (RK_TIMER_READVALUE(TIMER_CLKEVT) > cycles); + } while (rk30_timer_read_current_value(TIMER_CLKEVT) > cycles); return 0; } static void rk30_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - u32 count; - switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - count = 24000000 / HZ - 1; - do { - RK_TIMER_DISABLE(TIMER_CLKEVT); - RK_TIMER_SETCOUNT(TIMER_CLKEVT, count); - RK_TIMER_ENABLE(TIMER_CLKEVT); - } while (RK_TIMER_READVALUE(TIMER_CLKEVT) > count); + rk30_timer_set_next_event(24000000 / HZ - 1, evt); break; case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_ONESHOT: @@ -136,7 +149,9 @@ static __init int rk30_timer_init_clockevent(void) { struct clock_event_device *ce = &rk30_timer_clockevent; struct clk *clk = clk_get(NULL, TIMER_CLKEVT_NAME); + struct clk *pclk = clk_get(NULL, "pclk_" TIMER_CLKEVT_NAME); + clk_enable(pclk); clk_enable(clk); RK_TIMER_DISABLE(TIMER_CLKEVT); @@ -152,9 +167,15 @@ static __init int rk30_timer_init_clockevent(void) static cycle_t rk30_timer_read(struct clocksource *cs) { - return ~RK_TIMER_READVALUE(TIMER_CLKSRC); + return ~rk30_timer_read_current_value(TIMER_CLKSRC); } +/* + * Constants generated by clocksource_hz2mult(24000000, 26). + * This gives a resolution of about 41ns and a wrap period of about 178s. + */ +#define MULT 2796202667u +#define SHIFT 26 #define MASK (u32)~0 static struct clocksource rk30_timer_clocksource = { @@ -170,36 +191,41 @@ static void __init rk30_timer_init_clocksource(void) static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n"; struct clocksource *cs = &rk30_timer_clocksource; struct clk *clk = clk_get(NULL, TIMER_CLKSRC_NAME); + struct clk *pclk = clk_get(NULL, "pclk_" TIMER_CLKSRC_NAME); + clk_enable(pclk); clk_enable(clk); RK_TIMER_DISABLE(TIMER_CLKSRC); + clk_disable(clk); RK_TIMER_SETCOUNT(TIMER_CLKSRC, 0xFFFFFFFF); RK_TIMER_ENABLE_FREE_RUNNING(TIMER_CLKSRC); + clk_enable(clk); if (clocksource_register_hz(cs, 24000000)) printk(err, cs->name); } +#ifdef CONFIG_HAVE_SCHED_CLOCK static DEFINE_CLOCK_DATA(cd); unsigned long long notrace sched_clock(void) { - u32 cyc = ~RK_TIMER_READVALUE(TIMER_CLKSRC); - const struct clocksource *cs = &rk30_timer_clocksource; - return cyc_to_fixed_sched_clock(&cd, cyc, MASK, cs->mult, cs->shift); + u32 cyc = ~rk30_timer_read_current_value(TIMER_CLKSRC); + return cyc_to_fixed_sched_clock(&cd, cyc, MASK, MULT, SHIFT); } static void notrace rk30_update_sched_clock(void) { - u32 cyc = ~RK_TIMER_READVALUE(TIMER_CLKSRC); + u32 cyc = ~rk30_timer_read_current_value(TIMER_CLKSRC); update_sched_clock(&cd, cyc, MASK); } static void __init rk30_sched_clock_init(void) { - init_sched_clock(&cd, rk30_update_sched_clock, 32, 24000000); + init_fixed_sched_clock(&cd, rk30_update_sched_clock, 32, 24000000, MULT, SHIFT); } +#endif static void __init rk30_timer_init(void) { @@ -208,7 +234,9 @@ static void __init rk30_timer_init(void) #endif rk30_timer_init_clocksource(); rk30_timer_init_clockevent(); +#ifdef CONFIG_HAVE_SCHED_CLOCK rk30_sched_clock_init(); +#endif } struct sys_timer rk30_timer = { -- 2.34.1