Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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.

+2 −52
Original line number Diff line number Diff line
@@ -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:
+34 −51
Original line number Diff line number Diff line
/*
 *  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;

+5 −2
Original line number Diff line number Diff line
@@ -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 */
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;

@@ -46,6 +48,7 @@ u32 arch_gettimeoffset(void)
{
       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();
}