summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesper Nilsson <jesper.nilsson@axis.com>2010-07-30 17:33:07 +0200
committerJesper Nilsson <jesper.nilsson@axis.com>2010-08-04 12:58:55 +0200
commit60dbd6633178a8625ed71329da0167c6d50c559c (patch)
treebcbc1004dfe356ce7cf30f183725dbbc9dad3e4f
parent26bfeea38a4a5daf52c8f01c986ca8680bf1f6a1 (diff)
CRIS: GENERIC_TIME fixes
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>
-rw-r--r--arch/cris/Kconfig5
-rw-r--r--arch/cris/arch-v10/kernel/time.c54
-rw-r--r--arch/cris/arch-v32/kernel/time.c85
-rw-r--r--arch/cris/kernel/time.c7
4 files changed, 45 insertions, 106 deletions
diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig
index e25bf4440b51..4827c72b9634 100644
--- a/arch/cris/Kconfig
+++ b/arch/cris/Kconfig
@@ -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.
diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c
index 30adae594aef..00eb36f8debf 100644
--- a/arch/cris/arch-v10/kernel/time.c
+++ b/arch/cris/arch-v10/kernel/time.c
@@ -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:
diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c
index 1ee0e1010228..a545211e999d 100644
--- a/arch/cris/arch-v32/kernel/time.c
+++ b/arch/cris/arch-v32/kernel/time.c
@@ -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>
@@ -36,6 +36,30 @@
/* 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;
diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c
index c72730d20ef6..b5096430ce1c 100644
--- a/arch/cris/kernel/time.c
+++ b/arch/cris/kernel/time.c
@@ -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();
}