2014-12-21 21:46:56 +03:00
/*
* linux / kernel / time / timecounter . c
*
* based on code that migrated away from
* linux / kernel / time / clocksource . c
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/export.h>
# include <linux/timecounter.h>
void timecounter_init ( struct timecounter * tc ,
const struct cyclecounter * cc ,
u64 start_tstamp )
{
tc - > cc = cc ;
tc - > cycle_last = cc - > read ( cc ) ;
tc - > nsec = start_tstamp ;
2014-12-21 21:47:06 +03:00
tc - > mask = ( 1ULL < < cc - > shift ) - 1 ;
tc - > frac = 0 ;
2014-12-21 21:46:56 +03:00
}
EXPORT_SYMBOL_GPL ( timecounter_init ) ;
/**
* timecounter_read_delta - get nanoseconds since last call of this function
* @ tc : Pointer to time counter
*
* When the underlying cycle counter runs over , this will be handled
* correctly as long as it does not run over more than once between
* calls .
*
* The first call to this function for a new time counter initializes
* the time tracking and returns an undefined result .
*/
static u64 timecounter_read_delta ( struct timecounter * tc )
{
2016-12-21 22:32:01 +03:00
u64 cycle_now , cycle_delta ;
2014-12-21 21:46:56 +03:00
u64 ns_offset ;
/* read cycle counter: */
cycle_now = tc - > cc - > read ( tc - > cc ) ;
/* calculate the delta since the last timecounter_read_delta(): */
cycle_delta = ( cycle_now - tc - > cycle_last ) & tc - > cc - > mask ;
/* convert to nanoseconds: */
2014-12-21 21:47:06 +03:00
ns_offset = cyclecounter_cyc2ns ( tc - > cc , cycle_delta ,
tc - > mask , & tc - > frac ) ;
2014-12-21 21:46:56 +03:00
/* update time stamp of timecounter_read_delta() call: */
tc - > cycle_last = cycle_now ;
return ns_offset ;
}
u64 timecounter_read ( struct timecounter * tc )
{
u64 nsec ;
/* increment time by nanoseconds since last call */
nsec = timecounter_read_delta ( tc ) ;
nsec + = tc - > nsec ;
tc - > nsec = nsec ;
return nsec ;
}
EXPORT_SYMBOL_GPL ( timecounter_read ) ;
2014-12-21 21:47:06 +03:00
/*
* This is like cyclecounter_cyc2ns ( ) , but it is used for computing a
* time previous to the time stored in the cycle counter .
*/
static u64 cc_cyc2ns_backwards ( const struct cyclecounter * cc ,
2016-12-21 22:32:01 +03:00
u64 cycles , u64 mask , u64 frac )
2014-12-21 21:47:06 +03:00
{
u64 ns = ( u64 ) cycles ;
ns = ( ( ns * cc - > mult ) - frac ) > > cc - > shift ;
return ns ;
}
2014-12-21 21:46:56 +03:00
u64 timecounter_cyc2time ( struct timecounter * tc ,
2016-12-21 22:32:01 +03:00
u64 cycle_tstamp )
2014-12-21 21:46:56 +03:00
{
2014-12-21 21:47:06 +03:00
u64 delta = ( cycle_tstamp - tc - > cycle_last ) & tc - > cc - > mask ;
u64 nsec = tc - > nsec , frac = tc - > frac ;
2014-12-21 21:46:56 +03:00
/*
* Instead of always treating cycle_tstamp as more recent
* than tc - > cycle_last , detect when it is too far in the
* future and treat it as old time stamp instead .
*/
2014-12-21 21:47:06 +03:00
if ( delta > tc - > cc - > mask / 2 ) {
delta = ( tc - > cycle_last - cycle_tstamp ) & tc - > cc - > mask ;
nsec - = cc_cyc2ns_backwards ( tc - > cc , delta , tc - > mask , frac ) ;
2014-12-21 21:46:56 +03:00
} else {
2014-12-21 21:47:06 +03:00
nsec + = cyclecounter_cyc2ns ( tc - > cc , delta , tc - > mask , & frac ) ;
2014-12-21 21:46:56 +03:00
}
return nsec ;
}
EXPORT_SYMBOL_GPL ( timecounter_cyc2time ) ;