CRIS: GENERIC_TIME fixes
authorJesper Nilsson <jesper.nilsson@axis.com>
Fri, 30 Jul 2010 15:33:07 +0000 (17:33 +0200)
committerJesper Nilsson <jesper.nilsson@axis.com>
Wed, 4 Aug 2010 10:58:55 +0000 (12:58 +0200)
GENERIC_TIME was not functional for CRIS, giving random backward
time jumps.

For CRISv32 implement a new clocksource using the free running counter
and ditch the arch_gettimeoffset.

The random time jumps still existed, but turned out to be the write_seqlock
which was missing around our do_timer() call.

So switch over to GENERIC_TIME using the clocksource for CRISv32.

CRISv10 doesn't have the free running counter needed for the
clocksource trick, but we can still use GENERIC_TIME with
arch_gettimeoffset.

Unfortunately, there were problems in using the prescaler register
to timer0 for the gettimeoffset calculation, so it is now ignored,
making our resolution worse by the tune of 40usec (0.4%) worst case.

At the same time, clean up some formatting and use NSEC_PER_SEC
instead of 1000000000.

Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
arch/cris/Kconfig
arch/cris/arch-v10/kernel/time.c
arch/cris/arch-v32/kernel/time.c
arch/cris/kernel/time.c

index e25bf4440b513d215ae183736268178143289b1d..4827c72b963482f812fa45469dc30605f3995cad 100644 (file)
@@ -27,7 +27,7 @@ config GENERIC_CMOS_UPDATE
        def_bool y
 
 config ARCH_USES_GETTIMEOFFSET
-       def_bool y
+       def_bool n
 
 config GENERIC_IOMAP
        bool
@@ -131,16 +131,19 @@ choice
 
 config ETRAX100LX
        bool "ETRAX-100LX-v1"
+       select ARCH_USES_GETTIMEOFFSET
        help
          Support version 1 of the ETRAX 100LX.
 
 config ETRAX100LX_V2
        bool "ETRAX-100LX-v2"
+       select ARCH_USES_GETTIMEOFFSET
        help
          Support version 2 of the ETRAX 100LX.
 
 config SVINTO_SIM
        bool "ETRAX-100LX-for-xsim-simulator"
+       select ARCH_USES_GETTIMEOFFSET
        help
          Support the xsim ETRAX Simulator.
 
index 30adae594aef6743eec26d31ac887ec6b8f5ff7c..00eb36f8debf10484912f35fb12af2452eb947fe 100644 (file)
@@ -61,66 +61,16 @@ unsigned long get_ns_in_jiffie(void)
 
 unsigned long do_slow_gettimeoffset(void)
 {
-       unsigned long count, t1;
-       unsigned long usec_count = 0;
-       unsigned short presc_count;
-
-       static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */
-       static unsigned long jiffies_p = 0;
-
-       /*
-        * cache volatile jiffies temporarily; we have IRQs turned off. 
-        */
-       unsigned long jiffies_t;
+       unsigned long count;
 
        /* The timer interrupt comes from Etrax timer 0. In order to get
         * better precision, we check the current value. It might have
         * underflowed already though.
         */
-
-#ifndef CONFIG_SVINTO_SIM
-       /* Not available in the xsim simulator. */
        count = *R_TIMER0_DATA;
-       presc_count = *R_TIM_PRESC_STATUS;  
-       /* presc_count might be wrapped */
-       t1 = *R_TIMER0_DATA;
-       if (count != t1){
-               /* it wrapped, read prescaler again...  */
-               presc_count = *R_TIM_PRESC_STATUS;
-               count = t1;
-       }
-#else
-       count = 0;
-       presc_count = 0;
-#endif
-
-       jiffies_t = jiffies;
 
-       /*
-        * avoiding timer inconsistencies (they are rare, but they happen)...
-        * there are one problem that must be avoided here:
-        *  1. the timer counter underflows
-        */
-       if( jiffies_t == jiffies_p ) {
-               if( count > count_p ) {
-                       /* Timer wrapped, use new count and prescale 
-                        * increase the time corresponding to one jiffie
-                        */
-                       usec_count = 1000000/HZ;
-               }
-       } else
-               jiffies_p = jiffies_t;
-        count_p = count;
-       if (presc_count >= PRESCALE_VALUE/2 ){
-               presc_count =  PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2;
-       } else {
-               presc_count =  PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2;
-       }
        /* Convert timer value to usec */
-       usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) +
-                     (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000);
-
-       return usec_count;
+       return (TIMER0_DIV - count) * ((NSEC_PER_SEC/1000)/HZ)/TIMER0_DIV;
 }
 
 /* Excerpt from the Etrax100 HSDD about the built-in watchdog:
index 1ee0e1010228af2eab17d71a5e534dc0dccda792..a545211e999d374dc49b9bc9ac04202c14f8f2ba 100644 (file)
@@ -1,13 +1,13 @@
 /*
  *  linux/arch/cris/arch-v32/kernel/time.c
  *
- *  Copyright (C) 2003-2007 Axis Communications AB
+ *  Copyright (C) 2003-2010 Axis Communications AB
  *
  */
 
 #include <linux/timex.h>
 #include <linux/time.h>
-#include <linux/jiffies.h>
+#include <linux/clocksource.h>
 #include <linux/interrupt.h>
 #include <linux/swap.h>
 #include <linux/sched.h>
 /* Number of 763 counts before watchdog bites */
 #define ETRAX_WD_CNT           ((2*ETRAX_WD_HZ)/HZ + 1)
 
+/* Register the continuos readonly timer available in FS and ARTPEC-3.  */
+static cycle_t read_cont_rotime(struct clocksource *cs)
+{
+       return (u32)REG_RD(timer, regi_timer0, r_time);
+}
+
+static struct clocksource cont_rotime = {
+       .name   = "crisv32_rotime",
+       .rating = 300,
+       .read   = read_cont_rotime,
+       .mask   = CLOCKSOURCE_MASK(32),
+       .shift  = 10,
+       .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int __init etrax_init_cont_rotime(void)
+{
+       cont_rotime.mult = clocksource_khz2mult(100000, cont_rotime.shift);
+       clocksource_register(&cont_rotime);
+       return 0;
+}
+arch_initcall(etrax_init_cont_rotime);
+
+
 unsigned long timer_regs[NR_CPUS] =
 {
        regi_timer0,
@@ -67,43 +91,6 @@ unsigned long get_ns_in_jiffie(void)
        return ns;
 }
 
-unsigned long do_slow_gettimeoffset(void)
-{
-       unsigned long count;
-       unsigned long usec_count = 0;
-
-       /* For the first call after boot */
-       static unsigned long count_p = TIMER0_DIV;
-       static unsigned long jiffies_p = 0;
-
-       /* Cache volatile jiffies temporarily; we have IRQs turned off. */
-       unsigned long jiffies_t;
-
-       /* The timer interrupt comes from Etrax timer 0. In order to get
-        * better precision, we check the current value. It might have
-        * underflowed already though. */
-       count = REG_RD(timer, regi_timer0, r_tmr0_data);
-       jiffies_t = jiffies;
-
-       /* Avoiding timer inconsistencies (they are rare, but they happen)
-        * There is one problem that must be avoided here:
-        *      1. the timer counter underflows
-        */
-       if( jiffies_t == jiffies_p ) {
-               if( count > count_p ) {
-                       /* Timer wrapped, use new count and prescale.
-                        * Increase the time corresponding to one jiffy.
-                        */
-                       usec_count = 1000000/HZ;
-               }
-       } else
-               jiffies_p = jiffies_t;
-        count_p = count;
-       /* Convert timer value to usec */
-       /* 100 MHz timer, divide by 100 to get usec */
-       usec_count +=  (TIMER0_DIV - count) / 100;
-       return usec_count;
-}
 
 /* From timer MDS describing the hardware watchdog:
  * 4.3.1 Watchdog Operation
@@ -126,8 +113,7 @@ static short int watchdog_key = 42;  /* arbitrary 7 bit number */
  * is used though, so set this really low. */
 #define WATCHDOG_MIN_FREE_PAGES 8
 
-void
-reset_watchdog(void)
+void reset_watchdog(void)
 {
 #if defined(CONFIG_ETRAX_WATCHDOG)
        reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
@@ -147,8 +133,7 @@ reset_watchdog(void)
 
 /* stop the watchdog - we still need the correct key */
 
-void
-stop_watchdog(void)
+void stop_watchdog(void)
 {
 #if defined(CONFIG_ETRAX_WATCHDOG)
        reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
@@ -162,8 +147,7 @@ stop_watchdog(void)
 
 extern void show_registers(struct pt_regs *regs);
 
-void
-handle_watchdog_bite(struct pt_regs* regs)
+void handle_watchdog_bite(struct pt_regs *regs)
 {
 #if defined(CONFIG_ETRAX_WATCHDOG)
        extern int cause_of_death;
@@ -203,8 +187,7 @@ handle_watchdog_bite(struct pt_regs* regs)
  */
 extern void cris_do_profile(struct pt_regs *regs);
 
-static inline irqreturn_t
-timer_interrupt(int irq, void *dev_id)
+static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
        struct pt_regs *regs = get_irq_regs();
        int cpu = smp_processor_id();
@@ -233,7 +216,9 @@ timer_interrupt(int irq, void *dev_id)
                return IRQ_HANDLED;
 
        /* Call the real timer interrupt handler */
+       write_seqlock(&xtime_lock);
        do_timer(1);
+       write_sequnlock(&xtime_lock);
         return IRQ_HANDLED;
 }
 
@@ -246,8 +231,7 @@ static struct irqaction irq_timer = {
        .name = "timer"
 };
 
-void __init
-cris_timer_init(void)
+void __init cris_timer_init(void)
 {
        int cpu = smp_processor_id();
        reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
@@ -273,8 +257,7 @@ cris_timer_init(void)
        REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask);
 }
 
-void __init
-time_init(void)
+void __init time_init(void)
 {
        reg_intr_vect_rw_mask intr_mask;
 
index c72730d20ef68e03a1e836968e29f5cefd61bfdf..b5096430ce1c770ebad27672eb0e7fc029a6e211 100644 (file)
@@ -39,13 +39,16 @@ int have_rtc;  /* used to remember if we have an RTC or not */;
 extern unsigned long loops_per_jiffy; /* init/main.c */
 unsigned long loops_per_usec;
 
+
+#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
 extern unsigned long do_slow_gettimeoffset(void);
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
 
 u32 arch_gettimeoffset(void)
 {
-       return do_gettimeoffset() * 1000;
+       return do_gettimeoffset() * 1000;
 }
+#endif
 
 /*
  * BUG: This routine does not handle hour overflow properly; it just
@@ -151,7 +154,7 @@ cris_do_profile(struct pt_regs* regs)
 
 unsigned long long sched_clock(void)
 {
-       return (unsigned long long)jiffies * (1000000000 / HZ) +
+       return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ) +
                get_ns_in_jiffie();
 }