2005-10-20 03:23:26 +04:00
/*
* Common time prototypes and such for all ppc machines .
*
* Written by Cort Dougan ( cort @ cs . nmt . edu ) to merge
* Paul Mackerras ' version and mine for PReP and Pmac .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# ifndef __POWERPC_TIME_H
# define __POWERPC_TIME_H
# ifdef __KERNEL__
# include <linux/types.h>
# include <linux/percpu.h>
# include <asm/processor.h>
/* time.c */
extern unsigned long tb_ticks_per_jiffy ;
extern unsigned long tb_ticks_per_usec ;
extern unsigned long tb_ticks_per_sec ;
2012-04-18 10:01:19 +04:00
extern struct clock_event_device decrementer_clockevent ;
2005-10-20 03:23:26 +04:00
struct rtc_time ;
extern void to_tm ( int tim , struct rtc_time * tm ) ;
2008-05-08 08:27:19 +04:00
extern void GregorianDay ( struct rtc_time * tm ) ;
2005-10-20 03:23:26 +04:00
extern void generic_calibrate_decr ( void ) ;
2007-01-24 22:41:24 +03:00
extern void set_dec_cpu6 ( unsigned int val ) ;
2005-10-20 03:23:26 +04:00
/* Some sane defaults: 125 MHz timebase, 1GHz processor */
extern unsigned long ppc_proc_freq ;
# define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
extern unsigned long ppc_tb_freq ;
# define DEFAULT_TB_FREQ 125000000UL
struct div_result {
u64 result_high ;
u64 result_low ;
} ;
/* Accessor functions for the timebase (RTC on 601) registers. */
/* If one day CONFIG_POWER is added just define __USE_RTC as 1 */
# ifdef CONFIG_6xx
2005-10-20 16:33:06 +04:00
# define __USE_RTC() (!cpu_has_feature(CPU_FTR_USE_TB))
2005-10-20 03:23:26 +04:00
# else
# define __USE_RTC() 0
# endif
2006-10-20 08:37:05 +04:00
# ifdef CONFIG_PPC64
/* For compatibility, get_tbl() is defined as get_tb() on ppc64 */
# define get_tbl get_tb
# else
2005-10-20 03:23:26 +04:00
static inline unsigned long get_tbl ( void )
{
# if defined(CONFIG_403GCX)
2006-10-20 08:37:05 +04:00
unsigned long tbl ;
2005-10-20 03:23:26 +04:00
asm volatile ( " mfspr %0, 0x3dd " : " =r " ( tbl ) ) ;
2006-10-20 08:37:05 +04:00
return tbl ;
2005-10-20 03:23:26 +04:00
# else
2006-10-20 08:37:05 +04:00
return mftbl ( ) ;
2005-10-20 03:23:26 +04:00
# endif
}
static inline unsigned int get_tbu ( void )
{
2006-10-20 08:37:05 +04:00
# ifdef CONFIG_403GCX
2005-10-20 03:23:26 +04:00
unsigned int tbu ;
asm volatile ( " mfspr %0, 0x3dc " : " =r " ( tbu ) ) ;
2006-10-20 08:37:05 +04:00
return tbu ;
2005-10-20 03:23:26 +04:00
# else
2006-10-20 08:37:05 +04:00
return mftbu ( ) ;
2005-10-20 03:23:26 +04:00
# endif
}
2006-10-20 08:37:05 +04:00
# endif /* !CONFIG_PPC64 */
2005-10-20 03:23:26 +04:00
static inline unsigned int get_rtcl ( void )
{
unsigned int rtcl ;
asm volatile ( " mfrtcl %0 " : " =r " ( rtcl ) ) ;
return rtcl ;
}
2005-10-23 11:14:56 +04:00
static inline u64 get_rtc ( void )
{
unsigned int hi , lo , hi2 ;
do {
asm volatile ( " mfrtcu %0; mfrtcl %1; mfrtcu %2 "
: " =r " ( hi ) , " =r " ( lo ) , " =r " ( hi2 ) ) ;
} while ( hi2 ! = hi ) ;
return ( u64 ) hi * 1000000000 + lo ;
}
2005-10-20 03:23:26 +04:00
# ifdef CONFIG_PPC64
static inline u64 get_tb ( void )
{
return mftb ( ) ;
}
2006-10-20 08:37:05 +04:00
# else /* CONFIG_PPC64 */
2005-10-20 03:23:26 +04:00
static inline u64 get_tb ( void )
{
unsigned int tbhi , tblo , tbhi2 ;
do {
tbhi = get_tbu ( ) ;
tblo = get_tbl ( ) ;
tbhi2 = get_tbu ( ) ;
} while ( tbhi ! = tbhi2 ) ;
return ( ( u64 ) tbhi < < 32 ) | tblo ;
}
2006-10-20 08:37:05 +04:00
# endif /* !CONFIG_PPC64 */
2005-10-20 03:23:26 +04:00
2007-09-19 08:21:56 +04:00
static inline u64 get_tb_or_rtc ( void )
{
return __USE_RTC ( ) ? get_rtc ( ) : get_tb ( ) ;
}
2005-10-20 03:23:26 +04:00
static inline void set_tb ( unsigned int upper , unsigned int lower )
{
mtspr ( SPRN_TBWL , 0 ) ;
mtspr ( SPRN_TBWU , upper ) ;
mtspr ( SPRN_TBWL , lower ) ;
}
/* Accessor functions for the decrementer register.
* The 4 xx doesn ' t even have a decrementer . I tried to use the
* generic timer interrupt code , which seems OK , with the 4 xx PIT
* in auto - reload mode . The problem is PIT stops counting when it
* hits zero . If it would wrap , we could use it just like a decrementer .
*/
static inline unsigned int get_dec ( void )
{
# if defined(CONFIG_40x)
return ( mfspr ( SPRN_PIT ) ) ;
# else
return ( mfspr ( SPRN_DEC ) ) ;
# endif
}
2007-10-31 14:25:35 +03:00
/*
* Note : Book E and 4 xx processors differ from other PowerPC processors
* in when the decrementer generates its interrupt : on the 1 to 0
* transition for Book E / 4 xx , but on the 0 to - 1 transition for others .
*/
2005-10-20 03:23:26 +04:00
static inline void set_dec ( int val )
{
# if defined(CONFIG_40x)
2007-08-20 16:29:11 +04:00
mtspr ( SPRN_PIT , val ) ;
2005-10-20 03:23:26 +04:00
# elif defined(CONFIG_8xx_CPU6)
2007-10-31 14:25:35 +03:00
set_dec_cpu6 ( val - 1 ) ;
2005-10-20 03:23:26 +04:00
# else
2007-10-31 14:25:35 +03:00
# ifndef CONFIG_BOOKE
- - val ;
2005-10-20 03:23:26 +04:00
# endif
2007-10-31 14:25:35 +03:00
mtspr ( SPRN_DEC , val ) ;
2005-10-20 03:23:26 +04:00
# endif /* not 40x or 8xx_CPU6 */
}
static inline unsigned long tb_ticks_since ( unsigned long tstamp )
{
if ( __USE_RTC ( ) ) {
int delta = get_rtcl ( ) - ( unsigned int ) tstamp ;
return delta < 0 ? delta + 1000000000 : delta ;
}
return get_tbl ( ) - tstamp ;
}
# define mulhwu(x,y) \
( { unsigned z ; asm ( " mulhwu %0,%1,%2 " : " =r " ( z ) : " r " ( x ) , " r " ( y ) ) ; z ; } )
# ifdef CONFIG_PPC64
# define mulhdu(x,y) \
( { unsigned long z ; asm ( " mulhdu %0,%1,%2 " : " =r " ( z ) : " r " ( x ) , " r " ( y ) ) ; z ; } )
# else
extern u64 mulhdu ( u64 , u64 ) ;
# endif
2005-10-22 08:55:23 +04:00
extern void div128_by_32 ( u64 dividend_high , u64 dividend_low ,
unsigned divisor , struct div_result * dr ) ;
2005-10-20 03:23:26 +04:00
/* Used to store Processor Utilization register (purr) values */
struct cpu_usage {
u64 current_tb ; /* Holds the current purr register values */
} ;
DECLARE_PER_CPU ( struct cpu_usage , cpu_usage_array ) ;
2007-09-21 07:26:03 +04:00
extern void secondary_cpu_time_init ( void ) ;
2007-06-22 10:54:30 +04:00
powerpc/time: Optimise decrementer_check_overflow
decrementer_check_overflow is called from arch_local_irq_restore so
we want to make it as light weight as possible. As such, turn
decrementer_check_overflow into an inline function.
To avoid a circular mess of includes, separate out the two components
of struct decrementer_clock and keep the struct clock_event_device
part local to time.c.
The fast path improves from:
arch_local_irq_restore
0: mflr r0
4: std r0,16(r1)
8: stdu r1,-112(r1)
c: stb r3,578(r13)
10: cmpdi cr7,r3,0
14: beq- cr7,24 <.arch_local_irq_restore+0x24>
...
24: addi r1,r1,112
28: ld r0,16(r1)
2c: mtlr r0
30: blr
to:
arch_local_irq_restore
0: std r30,-16(r1)
4: ld r30,0(r2)
8: stb r3,578(r13)
c: cmpdi cr7,r3,0
10: beq- cr7,6c <.arch_local_irq_restore+0x6c>
...
6c: ld r30,-16(r1)
70: blr
Unfortunately we still setup a local TOC (due to -mminimal-toc). Yet
another sign we should be moving to -mcmodel=medium.
Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2011-11-24 00:07:22 +04:00
DECLARE_PER_CPU ( u64 , decrementers_next_tb ) ;
2011-11-24 00:07:17 +04:00
2005-10-20 03:23:26 +04:00
# endif /* __KERNEL__ */
2006-09-27 02:46:37 +04:00
# endif /* __POWERPC_TIME_H */