2009-01-09 03:46:40 +03:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2007 by Ralf Baechle
2012-08-21 22:45:05 +04:00
* Copyright ( C ) 2009 , 2012 Cavium , Inc .
2009-01-09 03:46:40 +03:00
*/
# include <linux/clocksource.h>
2011-07-29 02:46:31 +04:00
# include <linux/export.h>
2009-01-09 03:46:40 +03:00
# include <linux/init.h>
2010-10-08 03:03:47 +04:00
# include <linux/smp.h>
2009-01-09 03:46:40 +03:00
2010-10-08 03:03:47 +04:00
# include <asm/cpu-info.h>
2013-09-17 12:25:47 +04:00
# include <asm/cpu-type.h>
2009-01-09 03:46:40 +03:00
# include <asm/time.h>
# include <asm/octeon/octeon.h>
# include <asm/octeon/cvmx-ipd-defs.h>
2010-10-08 03:03:47 +04:00
# include <asm/octeon/cvmx-mio-defs.h>
2015-01-15 16:11:17 +03:00
# include <asm/octeon/cvmx-rst-defs.h>
2016-02-09 22:00:10 +03:00
# include <asm/octeon/cvmx-fpa-defs.h>
2012-08-21 22:45:05 +04:00
static u64 f ;
static u64 rdiv ;
static u64 sdiv ;
static u64 octeon_udelay_factor ;
static u64 octeon_ndelay_factor ;
void __init octeon_setup_delays ( void )
{
octeon_udelay_factor = octeon_get_clock_rate ( ) / 1000000 ;
/*
* For __ndelay we divide by 2 ^ 16 , so the factor is multiplied
* by the same amount .
*/
octeon_ndelay_factor = ( octeon_udelay_factor * 0x10000ull ) / 1000ull ;
preset_lpj = octeon_get_clock_rate ( ) / HZ ;
if ( current_cpu_type ( ) = = CPU_CAVIUM_OCTEON2 ) {
union cvmx_mio_rst_boot rst_boot ;
2015-01-15 16:11:17 +03:00
2012-08-21 22:45:05 +04:00
rst_boot . u64 = cvmx_read_csr ( CVMX_MIO_RST_BOOT ) ;
rdiv = rst_boot . s . c_mul ; /* CPU clock */
sdiv = rst_boot . s . pnr_mul ; /* I/O clock */
f = ( 0x8000000000000000ull / sdiv ) * 2 ;
2015-01-15 16:11:17 +03:00
} else if ( current_cpu_type ( ) = = CPU_CAVIUM_OCTEON3 ) {
union cvmx_rst_boot rst_boot ;
rst_boot . u64 = cvmx_read_csr ( CVMX_RST_BOOT ) ;
rdiv = rst_boot . s . c_mul ; /* CPU clock */
sdiv = rst_boot . s . pnr_mul ; /* I/O clock */
f = ( 0x8000000000000000ull / sdiv ) * 2 ;
2012-08-21 22:45:05 +04:00
}
2015-01-15 16:11:17 +03:00
2012-08-21 22:45:05 +04:00
}
2009-01-09 03:46:40 +03:00
/*
* Set the current core ' s cvmcount counter to the value of the
* IPD_CLK_COUNT . We do this on all cores as they are brought
* on - line . This allows for a read from a local cpu register to
* access a synchronized counter .
*
2010-10-08 03:03:47 +04:00
* On CPU_CAVIUM_OCTEON2 the IPD_CLK_COUNT is scaled by rdiv / sdiv .
2009-01-09 03:46:40 +03:00
*/
void octeon_init_cvmcount ( void )
{
2016-02-09 22:00:10 +03:00
u64 clk_reg ;
2009-01-09 03:46:40 +03:00
unsigned long flags ;
unsigned loops = 2 ;
2016-02-09 22:00:10 +03:00
clk_reg = octeon_has_feature ( OCTEON_FEATURE_FPA3 ) ?
CVMX_FPA_CLK_COUNT : CVMX_IPD_CLK_COUNT ;
2009-01-09 03:46:40 +03:00
/* Clobber loops so GCC will not unroll the following while loop. */
asm ( " " : " +r " ( loops ) ) ;
local_irq_save ( flags ) ;
/*
* Loop several times so we are executing from the cache ,
* which should give more deterministic timing .
*/
2010-10-08 03:03:47 +04:00
while ( loops - - ) {
2016-02-09 22:00:10 +03:00
u64 clk_count = cvmx_read_csr ( clk_reg ) ;
2010-10-08 03:03:47 +04:00
if ( rdiv ! = 0 ) {
2016-02-09 22:00:10 +03:00
clk_count * = rdiv ;
2010-10-08 03:03:47 +04:00
if ( f ! = 0 ) {
asm ( " dmultu \t %[cnt],%[f] \n \t "
" mfhi \t %[cnt] "
2016-02-09 22:00:10 +03:00
: [ cnt ] " +r " ( clk_count )
2012-08-21 22:45:05 +04:00
: [ f ] " r " ( f )
: " hi " , " lo " ) ;
2010-10-08 03:03:47 +04:00
}
}
2016-02-09 22:00:10 +03:00
write_c0_cvmcount ( clk_count ) ;
2010-10-08 03:03:47 +04:00
}
2009-01-09 03:46:40 +03:00
local_irq_restore ( flags ) ;
}
2009-04-22 23:06:06 +04:00
static cycle_t octeon_cvmcount_read ( struct clocksource * cs )
2009-01-09 03:46:40 +03:00
{
return read_c0_cvmcount ( ) ;
}
static struct clocksource clocksource_mips = {
. name = " OCTEON_CVMCOUNT " ,
. read = octeon_cvmcount_read ,
. mask = CLOCKSOURCE_MASK ( 64 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2009-12-24 00:18:54 +03:00
unsigned long long notrace sched_clock ( void )
{
2010-01-09 01:47:36 +03:00
/* 64-bit arithmatic can overflow, so use 128-bit. */
u64 t1 , t2 , t3 ;
unsigned long long rv ;
u64 mult = clocksource_mips . mult ;
u64 shift = clocksource_mips . shift ;
u64 cnt = read_c0_cvmcount ( ) ;
asm (
" dmultu \t %[cnt],%[mult] \n \t "
" nor \t %[t1],$0,%[shift] \n \t "
" mfhi \t %[t2] \n \t "
" mflo \t %[t3] \n \t "
" dsll \t %[t2],%[t2],1 \n \t "
" dsrlv \t %[rv],%[t3],%[shift] \n \t "
" dsllv \t %[t1],%[t2],%[t1] \n \t "
" or \t %[rv],%[t1],%[rv] \n \t "
: [ rv ] " =&r " ( rv ) , [ t1 ] " =&r " ( t1 ) , [ t2 ] " =&r " ( t2 ) , [ t3 ] " =&r " ( t3 )
: [ cnt ] " r " ( cnt ) , [ mult ] " r " ( mult ) , [ shift ] " r " ( shift )
: " hi " , " lo " ) ;
return rv ;
2009-12-24 00:18:54 +03:00
}
2009-01-09 03:46:40 +03:00
void __init plat_time_init ( void )
{
clocksource_mips . rating = 300 ;
2010-04-27 07:23:11 +04:00
clocksource_register_hz ( & clocksource_mips , octeon_get_clock_rate ( ) ) ;
2009-01-09 03:46:40 +03:00
}
2010-06-02 00:18:15 +04:00
void __udelay ( unsigned long us )
{
u64 cur , end , inc ;
cur = read_c0_cvmcount ( ) ;
inc = us * octeon_udelay_factor ;
end = cur + inc ;
while ( end > cur )
cur = read_c0_cvmcount ( ) ;
}
EXPORT_SYMBOL ( __udelay ) ;
void __ndelay ( unsigned long ns )
{
u64 cur , end , inc ;
cur = read_c0_cvmcount ( ) ;
inc = ( ( ns * octeon_ndelay_factor ) > > 16 ) ;
end = cur + inc ;
while ( end > cur )
cur = read_c0_cvmcount ( ) ;
}
EXPORT_SYMBOL ( __ndelay ) ;
void __delay ( unsigned long loops )
{
u64 cur , end ;
cur = read_c0_cvmcount ( ) ;
end = cur + loops ;
while ( end > cur )
cur = read_c0_cvmcount ( ) ;
}
EXPORT_SYMBOL ( __delay ) ;
2012-08-21 22:45:05 +04:00
/**
* octeon_io_clk_delay - wait for a given number of io clock cycles to pass .
*
* We scale the wait by the clock ratio , and then wait for the
* corresponding number of core clocks .
*
* @ count : The number of clocks to wait .
*/
void octeon_io_clk_delay ( unsigned long count )
{
u64 cur , end ;
cur = read_c0_cvmcount ( ) ;
if ( rdiv ! = 0 ) {
end = count * rdiv ;
if ( f ! = 0 ) {
asm ( " dmultu \t %[cnt],%[f] \n \t "
" mfhi \t %[cnt] "
: [ cnt ] " +r " ( end )
: [ f ] " r " ( f )
: " hi " , " lo " ) ;
}
end = cur + end ;
} else {
end = cur + count ;
}
while ( end > cur )
cur = read_c0_cvmcount ( ) ;
}
EXPORT_SYMBOL ( octeon_io_clk_delay ) ;