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>
This commit is contained in:
parent
26bfeea38a
commit
60dbd66331
@ -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
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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:
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
/* 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)
|
|||||||
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 */
|
|||||||
* 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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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 = {
|
|||||||
.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)
|
|||||||
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 */;
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user