Skip to content

Commit

Permalink
[MIPS] Fixup migration to GENERIC_TIME
Browse files Browse the repository at this point in the history
Since we already moved to GENERIC_TIME, we should implement alternatives
of old do_gettimeoffset routines to get sub-jiffies resolution from
gettimeofday().  This patch includes:

 * MIPS clocksource support (based on works by Manish Lachwani).
 * remove unused gettimeoffset routines and related codes.
 * remove unised 64bit do_div64_32().
 * simplify mips_hpt_init. (no argument needed, __init tag)
 * simplify c0_hpt_timer_init. (no need to write to c0_count)
 * remove some hpt_init routines.
 * mips_hpt_mask variable to specify bitmask of hpt value.
 * convert jmr3927_do_gettimeoffset to jmr3927_hpt_read.
 * convert ip27_do_gettimeoffset to ip27_hpt_read.
 * convert bcm1480_do_gettimeoffset to bcm1480_hpt_read.
 * simplify sb1250 hpt functions. (no need to subtract and shift)
    
Signed-off-by: Atsushi Nemoto <[email protected]>
Signed-off-by: Ralf Baechle <[email protected]>
  • Loading branch information
atsushi-nemoto authored and ralfbaechle committed Oct 31, 2006
1 parent 70e46f4 commit 16b7b2a
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 522 deletions.
39 changes: 8 additions & 31 deletions Documentation/mips/time.README
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,14 @@ The new time code provide the following services:

a) Implements functions required by Linux common code:
time_init
do_gettimeofday
do_settimeofday

b) provides an abstraction of RTC and null RTC implementation as default.
extern unsigned long (*rtc_get_time)(void);
extern int (*rtc_set_time)(unsigned long);

c) a set of gettimeoffset functions for different CPUs and different
needs.

d) high-level and low-level timer interrupt routines where the timer
interrupt source may or may not be the CPU timer. The high-level
routine is dispatched through do_IRQ() while the low-level is
c) high-level and low-level timer interrupt routines where the timer
interrupt source may or may not be the CPU timer. The high-level
routine is dispatched through do_IRQ() while the low-level is
dispatched in assemably code (usually int-handler.S)


Expand All @@ -73,8 +68,7 @@ the following functions or values:
c) (optional) board-specific RTC routines.

d) (optional) mips_hpt_frequency - It must be definied if the board
is using CPU counter for timer interrupt or it is using fixed rate
gettimeoffset().
is using CPU counter for timer interrupt.


PORTING GUIDE
Expand All @@ -89,16 +83,6 @@ Step 1: decide how you like to implement the time services.
If the answer is no, you need a timer to provide the timer interrupt
at 100 HZ speed.

You cannot use the fast gettimeoffset functions, i.e.,

unsigned long fixed_rate_gettimeoffset(void);
unsigned long calibrate_div32_gettimeoffset(void);
unsigned long calibrate_div64_gettimeoffset(void);

You can use null_gettimeoffset() will gives the same time resolution as
jiffy. Or you can implement your own gettimeoffset (probably based on
some ad hoc hardware on your machine.)

c) The following sub steps assume your CPU has counter register.
Do you plan to use the CPU counter register as the timer interrupt
or use an exnternal timer?
Expand All @@ -123,8 +107,8 @@ Step 3: implement rtc routines, board_time_init() and plat_timer_setup()
board_time_init() -
a) (optional) set up RTC routines,
b) (optional) calibrate and set the mips_hpt_frequency
(only needed if you intended to use fixed_rate_gettimeoffset
or use cpu counter as timer interrupt source)
(only needed if you intended to use cpu counter as timer interrupt
source)

plat_timer_setup() -
a) (optional) over-write any choices made above by time_init().
Expand Down Expand Up @@ -154,8 +138,8 @@ for some of the functions in time.c.
For example, you may define your own timer interrupt routine, which does
some of its own processing and then calls timer_interrupt().

You can also over-ride any of the built-in functions (gettimeoffset,
RTC routines and/or timer interrupt routine).
You can also over-ride any of the built-in functions (RTC routines
and/or timer interrupt routine).


PORTING NOTES FOR SMP
Expand Down Expand Up @@ -187,10 +171,3 @@ You need to decide on your timer interrupt sources.

You can also do the low-level version of those interrupt routines,
following similar dispatching routes described above.

Note about do_gettimeoffset():

It is very likely the CPU counter registers are not sync'ed up in a SMP box.
Therefore you cannot really use the many of the existing routines that
are based on CPU counter. You should wirte your own gettimeoffset rouinte
if you want intra-jiffy resolution.
98 changes: 0 additions & 98 deletions arch/mips/au1000/common/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ static unsigned long r4k_cur; /* What counter should be at next timer irq */
int no_au1xxx_32khz;
extern int allow_au1k_wait; /* default off for CP0 Counter */

/* Cycle counter value at the previous timer interrupt.. */
static unsigned int timerhi = 0, timerlo = 0;

#ifdef CONFIG_PM
#if HZ < 100 || HZ > 1000
#error "unsupported HZ value! Must be in [100,1000]"
Expand Down Expand Up @@ -90,10 +87,6 @@ void mips_timer_interrupt(void)
goto null;

do {
count = read_c0_count();
timerhi += (count < timerlo); /* Wrap around */
timerlo = count;

kstat_this_cpu.irqs[irq]++;
do_timer(1);
#ifndef CONFIG_SMP
Expand Down Expand Up @@ -297,88 +290,6 @@ unsigned long cal_r4koff(void)
return (cpu_speed / HZ);
}

/* This is for machines which generate the exact clock. */
#define USECS_PER_JIFFY (1000000/HZ)
#define USECS_PER_JIFFY_FRAC (0x100000000LL*1000000/HZ&0xffffffff)

static unsigned long
div64_32(unsigned long v1, unsigned long v2, unsigned long v3)
{
unsigned long r0;
do_div64_32(r0, v1, v2, v3);
return r0;
}

static unsigned long do_fast_cp0_gettimeoffset(void)
{
u32 count;
unsigned long res, tmp;
unsigned long r0;

/* Last jiffy when do_fast_gettimeoffset() was called. */
static unsigned long last_jiffies=0;
unsigned long quotient;

/*
* Cached "1/(clocks per usec)*2^32" value.
* It has to be recalculated once each jiffy.
*/
static unsigned long cached_quotient=0;

tmp = jiffies;

quotient = cached_quotient;

if (tmp && last_jiffies != tmp) {
last_jiffies = tmp;
if (last_jiffies != 0) {
r0 = div64_32(timerhi, timerlo, tmp);
quotient = div64_32(USECS_PER_JIFFY, USECS_PER_JIFFY_FRAC, r0);
cached_quotient = quotient;
}
}

/* Get last timer tick in absolute kernel time */
count = read_c0_count();

/* .. relative to previous jiffy (32 bits is enough) */
count -= timerlo;

__asm__("multu\t%1,%2\n\t"
"mfhi\t%0"
: "=r" (res)
: "r" (count), "r" (quotient)
: "hi", "lo", GCC_REG_ACCUM);

/*
* Due to possible jiffies inconsistencies, we need to check
* the result so that we'll get a timer that is monotonic.
*/
if (res >= USECS_PER_JIFFY)
res = USECS_PER_JIFFY-1;

return res;
}

#ifdef CONFIG_PM
static unsigned long do_fast_pm_gettimeoffset(void)
{
unsigned long pc0;
unsigned long offset;

pc0 = au_readl(SYS_TOYREAD);
au_sync();
offset = pc0 - last_pc0;
if (offset > 2*MATCH20_INC) {
printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n",
(unsigned)offset, (unsigned)last_pc0,
(unsigned)last_match20, (unsigned)pc0);
}
offset = (unsigned long)((offset * 305) / 10);
return offset;
}
#endif

void __init plat_timer_setup(struct irqaction *irq)
{
unsigned int est_freq;
Expand Down Expand Up @@ -416,7 +327,6 @@ void __init plat_timer_setup(struct irqaction *irq)
unsigned int c0_status;

printk("WARNING: no 32KHz clock found.\n");
do_gettimeoffset = do_fast_cp0_gettimeoffset;

/* Ensure we get CPO_COUNTER interrupts.
*/
Expand All @@ -441,19 +351,11 @@ void __init plat_timer_setup(struct irqaction *irq)
while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
startup_match20_interrupt(counter0_irq);

do_gettimeoffset = do_fast_pm_gettimeoffset;

/* We can use the real 'wait' instruction.
*/
allow_au1k_wait = 1;
}

#else
/* We have to do this here instead of in timer_init because
* the generic code in arch/mips/kernel/time.c will write
* over our function pointer.
*/
do_gettimeoffset = do_fast_cp0_gettimeoffset;
#endif
}

Expand Down
9 changes: 1 addition & 8 deletions arch/mips/dec/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,6 @@ static unsigned int dec_ioasic_hpt_read(void)
return ioasic_read(IO_REG_FCTR);
}

static void dec_ioasic_hpt_init(unsigned int count)
{
ioasic_write(IO_REG_FCTR, ioasic_read(IO_REG_FCTR) - count);
}


void __init dec_time_init(void)
{
Expand All @@ -174,11 +169,9 @@ void __init dec_time_init(void)
mips_timer_state = dec_timer_state;
mips_timer_ack = dec_timer_ack;

if (!cpu_has_counter && IOASIC) {
if (!cpu_has_counter && IOASIC)
/* For pre-R4k systems we use the I/O ASIC's counter. */
mips_hpt_read = dec_ioasic_hpt_read;
mips_hpt_init = dec_ioasic_hpt_init;
}

/* Set up the rate of periodic DS1287 interrupts. */
CMOS_WRITE(RTC_REF_CLCK_32KHZ | (16 - __ffs(HZ)), RTC_REG_A);
Expand Down
40 changes: 8 additions & 32 deletions arch/mips/jmr3927/rbhma3100/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,25 +170,29 @@ static void jmr3927_machine_power_off(void)
while (1);
}

static unsigned int jmr3927_hpt_read(void)
{
/* We assume this function is called xtime_lock held. */
return jiffies * (JMR3927_TIMER_CLK / HZ) + jmr3927_tmrptr->trr;
}

#define USE_RTC_DS1742
#ifdef USE_RTC_DS1742
extern void rtc_ds1742_init(unsigned long base);
#endif
static void __init jmr3927_time_init(void)
{
mips_hpt_read = jmr3927_hpt_read;
mips_hpt_frequency = JMR3927_TIMER_CLK;
#ifdef USE_RTC_DS1742
if (jmr3927_have_nvram()) {
rtc_ds1742_init(JMR3927_IOC_NVRAMB_ADDR);
}
#endif
}

unsigned long jmr3927_do_gettimeoffset(void);

void __init plat_timer_setup(struct irqaction *irq)
{
do_gettimeoffset = jmr3927_do_gettimeoffset;

jmr3927_tmrptr->cpra = JMR3927_TIMER_CLK / HZ;
jmr3927_tmrptr->itmr = TXx927_TMTITMR_TIIE | TXx927_TMTITMR_TZCE;
jmr3927_tmrptr->ccdr = JMR3927_TIMER_CCD;
Expand All @@ -200,34 +204,6 @@ void __init plat_timer_setup(struct irqaction *irq)

#define USECS_PER_JIFFY (1000000/HZ)

unsigned long jmr3927_do_gettimeoffset(void)
{
unsigned long count;
unsigned long res = 0;

/* MUST read TRR before TISR. */
count = jmr3927_tmrptr->trr;

if (jmr3927_tmrptr->tisr & TXx927_TMTISR_TIIS) {
/* timer interrupt is pending. use Max value. */
res = USECS_PER_JIFFY - 1;
} else {
/* convert to usec */
/* res = count / (JMR3927_TIMER_CLK / 1000000); */
res = (count << 7) / ((JMR3927_TIMER_CLK << 7) / 1000000);

/*
* Due to possible jiffies inconsistencies, we need to check
* the result so that we'll get a timer that is monotonic.
*/
if (res >= USECS_PER_JIFFY)
res = USECS_PER_JIFFY-1;
}

return res;
}


//#undef DO_WRITE_THROUGH
#define DO_WRITE_THROUGH
#define DO_ENABLE_CACHE
Expand Down
Loading

0 comments on commit 16b7b2a

Please sign in to comment.