Commit 60dbd663 authored by Jesper Nilsson's avatar Jesper Nilsson

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: default avatarJesper Nilsson <jesper.nilsson@axis.com>
parent 26bfeea3
...@@ -27,7 +27,7 @@ config GENERIC_CMOS_UPDATE ...@@ -27,7 +27,7 @@ config GENERIC_CMOS_UPDATE
def_bool y def_bool y
config ARCH_USES_GETTIMEOFFSET config ARCH_USES_GETTIMEOFFSET
def_bool y def_bool n
config GENERIC_IOMAP config GENERIC_IOMAP
bool bool
...@@ -131,16 +131,19 @@ choice ...@@ -131,16 +131,19 @@ choice
config ETRAX100LX config ETRAX100LX
bool "ETRAX-100LX-v1" bool "ETRAX-100LX-v1"
select ARCH_USES_GETTIMEOFFSET
help help
Support version 1 of the ETRAX 100LX. Support version 1 of the ETRAX 100LX.
config ETRAX100LX_V2 config ETRAX100LX_V2
bool "ETRAX-100LX-v2" bool "ETRAX-100LX-v2"
select ARCH_USES_GETTIMEOFFSET
help help
Support version 2 of the ETRAX 100LX. Support version 2 of the ETRAX 100LX.
config SVINTO_SIM config SVINTO_SIM
bool "ETRAX-100LX-for-xsim-simulator" bool "ETRAX-100LX-for-xsim-simulator"
select ARCH_USES_GETTIMEOFFSET
help help
Support the xsim ETRAX Simulator. Support the xsim ETRAX Simulator.
......
...@@ -61,66 +61,16 @@ unsigned long get_ns_in_jiffie(void) ...@@ -61,66 +61,16 @@ unsigned long get_ns_in_jiffie(void)
unsigned long do_slow_gettimeoffset(void) unsigned long do_slow_gettimeoffset(void)
{ {
unsigned long count, t1; unsigned long count;
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;
/* The timer interrupt comes from Etrax timer 0. In order to get /* The timer interrupt comes from Etrax timer 0. In order to get
* better precision, we check the current value. It might have * better precision, we check the current value. It might have
* underflowed already though. * underflowed already though.
*/ */
#ifndef CONFIG_SVINTO_SIM
/* Not available in the xsim simulator. */
count = *R_TIMER0_DATA; 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 */ /* Convert timer value to usec */
usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) + return (TIMER0_DIV - count) * ((NSEC_PER_SEC/1000)/HZ)/TIMER0_DIV;
(( (presc_count) * (1000000000/PRESCALE_FREQ))/1000);
return usec_count;
} }
/* Excerpt from the Etrax100 HSDD about the built-in watchdog: /* Excerpt from the Etrax100 HSDD about the built-in watchdog:
......
/* /*
* linux/arch/cris/arch-v32/kernel/time.c * 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/timex.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/jiffies.h> #include <linux/clocksource.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -36,6 +36,30 @@ ...@@ -36,6 +36,30 @@
/* Number of 763 counts before watchdog bites */ /* Number of 763 counts before watchdog bites */
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) #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] = unsigned long timer_regs[NR_CPUS] =
{ {
regi_timer0, regi_timer0,
...@@ -67,43 +91,6 @@ unsigned long get_ns_in_jiffie(void) ...@@ -67,43 +91,6 @@ unsigned long get_ns_in_jiffie(void)
return ns; 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: /* From timer MDS describing the hardware watchdog:
* 4.3.1 Watchdog Operation * 4.3.1 Watchdog Operation
...@@ -126,8 +113,7 @@ static short int watchdog_key = 42; /* arbitrary 7 bit number */ ...@@ -126,8 +113,7 @@ static short int watchdog_key = 42; /* arbitrary 7 bit number */
* is used though, so set this really low. */ * is used though, so set this really low. */
#define WATCHDOG_MIN_FREE_PAGES 8 #define WATCHDOG_MIN_FREE_PAGES 8
void void reset_watchdog(void)
reset_watchdog(void)
{ {
#if defined(CONFIG_ETRAX_WATCHDOG) #if defined(CONFIG_ETRAX_WATCHDOG)
reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
...@@ -147,8 +133,7 @@ reset_watchdog(void) ...@@ -147,8 +133,7 @@ reset_watchdog(void)
/* stop the watchdog - we still need the correct key */ /* stop the watchdog - we still need the correct key */
void void stop_watchdog(void)
stop_watchdog(void)
{ {
#if defined(CONFIG_ETRAX_WATCHDOG) #if defined(CONFIG_ETRAX_WATCHDOG)
reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
...@@ -162,8 +147,7 @@ stop_watchdog(void) ...@@ -162,8 +147,7 @@ stop_watchdog(void)
extern void show_registers(struct pt_regs *regs); extern void show_registers(struct pt_regs *regs);
void void handle_watchdog_bite(struct pt_regs *regs)
handle_watchdog_bite(struct pt_regs* regs)
{ {
#if defined(CONFIG_ETRAX_WATCHDOG) #if defined(CONFIG_ETRAX_WATCHDOG)
extern int cause_of_death; extern int cause_of_death;
...@@ -203,8 +187,7 @@ handle_watchdog_bite(struct pt_regs* regs) ...@@ -203,8 +187,7 @@ handle_watchdog_bite(struct pt_regs* regs)
*/ */
extern void cris_do_profile(struct pt_regs *regs); extern void cris_do_profile(struct pt_regs *regs);
static inline irqreturn_t static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
timer_interrupt(int irq, void *dev_id)
{ {
struct pt_regs *regs = get_irq_regs(); struct pt_regs *regs = get_irq_regs();
int cpu = smp_processor_id(); int cpu = smp_processor_id();
...@@ -233,7 +216,9 @@ timer_interrupt(int irq, void *dev_id) ...@@ -233,7 +216,9 @@ timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
/* Call the real timer interrupt handler */ /* Call the real timer interrupt handler */
write_seqlock(&xtime_lock);
do_timer(1); do_timer(1);
write_sequnlock(&xtime_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -246,8 +231,7 @@ static struct irqaction irq_timer = { ...@@ -246,8 +231,7 @@ static struct irqaction irq_timer = {
.name = "timer" .name = "timer"
}; };
void __init void __init cris_timer_init(void)
cris_timer_init(void)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
...@@ -273,8 +257,7 @@ cris_timer_init(void) ...@@ -273,8 +257,7 @@ cris_timer_init(void)
REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask);
} }
void __init void __init time_init(void)
time_init(void)
{ {
reg_intr_vect_rw_mask intr_mask; reg_intr_vect_rw_mask intr_mask;
......
...@@ -39,6 +39,8 @@ int have_rtc; /* used to remember if we have an RTC or not */; ...@@ -39,6 +39,8 @@ int have_rtc; /* used to remember if we have an RTC or not */;
extern unsigned long loops_per_jiffy; /* init/main.c */ extern unsigned long loops_per_jiffy; /* init/main.c */
unsigned long loops_per_usec; unsigned long loops_per_usec;
#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
extern unsigned long do_slow_gettimeoffset(void); extern unsigned long do_slow_gettimeoffset(void);
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
...@@ -46,6 +48,7 @@ u32 arch_gettimeoffset(void) ...@@ -46,6 +48,7 @@ u32 arch_gettimeoffset(void)
{ {
return do_gettimeoffset() * 1000; return do_gettimeoffset() * 1000;
} }
#endif
/* /*
* BUG: This routine does not handle hour overflow properly; it just * BUG: This routine does not handle hour overflow properly; it just
...@@ -151,7 +154,7 @@ cris_do_profile(struct pt_regs* regs) ...@@ -151,7 +154,7 @@ cris_do_profile(struct pt_regs* regs)
unsigned long long sched_clock(void) 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(); get_ns_in_jiffie();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment