2013-03-26 04:43:11 +00:00
/*******************************************************************************
PTP 1588 clock using the STMMAC .
Copyright ( C ) 2013 Vayavya Labs Pvt Ltd
This program is free software ; you can redistribute it and / or modify it
under the terms and conditions of the GNU General Public License ,
version 2 , as published by the Free Software Foundation .
This program is distributed in the hope 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 .
The full GNU General Public License is included in this distribution in
the file called " COPYING " .
Author : Rayagond Kokatanur < rayagond @ vayavyalabs . com >
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "stmmac.h"
# include "stmmac_ptp.h"
/**
* stmmac_adjust_freq
*
* @ ptp : pointer to ptp_clock_info structure
* @ ppb : desired period change in parts ber billion
*
* Description : this function will adjust the frequency of hardware clock .
*/
static int stmmac_adjust_freq ( struct ptp_clock_info * ptp , s32 ppb )
{
struct stmmac_priv * priv =
container_of ( ptp , struct stmmac_priv , ptp_clock_ops ) ;
unsigned long flags ;
u32 diff , addend ;
int neg_adj = 0 ;
u64 adj ;
if ( ppb < 0 ) {
neg_adj = 1 ;
ppb = - ppb ;
}
addend = priv - > default_addend ;
adj = addend ;
adj * = ppb ;
diff = div_u64 ( adj , 1000000000ULL ) ;
addend = neg_adj ? ( addend - diff ) : ( addend + diff ) ;
spin_lock_irqsave ( & priv - > ptp_lock , flags ) ;
2018-04-16 16:08:15 +01:00
stmmac_config_addend ( priv , priv - > ptpaddr , addend ) ;
2013-12-20 11:19:34 -06:00
spin_unlock_irqrestore ( & priv - > ptp_lock , flags ) ;
2013-03-26 04:43:11 +00:00
return 0 ;
}
/**
* stmmac_adjust_time
*
* @ ptp : pointer to ptp_clock_info structure
* @ delta : desired change in nanoseconds
*
* Description : this function will shift / adjust the hardware clock time .
*/
static int stmmac_adjust_time ( struct ptp_clock_info * ptp , s64 delta )
{
struct stmmac_priv * priv =
container_of ( ptp , struct stmmac_priv , ptp_clock_ops ) ;
unsigned long flags ;
u32 sec , nsec ;
u32 quotient , reminder ;
int neg_adj = 0 ;
if ( delta < 0 ) {
neg_adj = 1 ;
delta = - delta ;
}
quotient = div_u64_rem ( delta , 1000000000ULL , & reminder ) ;
sec = quotient ;
nsec = reminder ;
spin_lock_irqsave ( & priv - > ptp_lock , flags ) ;
2018-04-16 16:08:15 +01:00
stmmac_adjust_systime ( priv , priv - > ptpaddr , sec , nsec , neg_adj ,
priv - > plat - > has_gmac4 ) ;
2013-12-20 11:19:34 -06:00
spin_unlock_irqrestore ( & priv - > ptp_lock , flags ) ;
2013-03-26 04:43:11 +00:00
return 0 ;
}
/**
* stmmac_get_time
*
* @ ptp : pointer to ptp_clock_info structure
* @ ts : pointer to hold time / result
*
* Description : this function will read the current time from the
* hardware clock and store it in @ ts .
*/
2015-03-29 23:12:07 +02:00
static int stmmac_get_time ( struct ptp_clock_info * ptp , struct timespec64 * ts )
2013-03-26 04:43:11 +00:00
{
struct stmmac_priv * priv =
container_of ( ptp , struct stmmac_priv , ptp_clock_ops ) ;
unsigned long flags ;
u64 ns ;
spin_lock_irqsave ( & priv - > ptp_lock , flags ) ;
2018-04-16 16:08:15 +01:00
stmmac_get_systime ( priv , priv - > ptpaddr , & ns ) ;
2013-03-26 04:43:11 +00:00
spin_unlock_irqrestore ( & priv - > ptp_lock , flags ) ;
2015-03-31 23:08:15 +02:00
* ts = ns_to_timespec64 ( ns ) ;
2013-03-26 04:43:11 +00:00
return 0 ;
}
/**
* stmmac_set_time
*
* @ ptp : pointer to ptp_clock_info structure
* @ ts : time value to set
*
* Description : this function will set the current time on the
* hardware clock .
*/
static int stmmac_set_time ( struct ptp_clock_info * ptp ,
2015-03-29 23:12:07 +02:00
const struct timespec64 * ts )
2013-03-26 04:43:11 +00:00
{
struct stmmac_priv * priv =
container_of ( ptp , struct stmmac_priv , ptp_clock_ops ) ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > ptp_lock , flags ) ;
2018-04-16 16:08:15 +01:00
stmmac_init_systime ( priv , priv - > ptpaddr , ts - > tv_sec , ts - > tv_nsec ) ;
2013-03-26 04:43:11 +00:00
spin_unlock_irqrestore ( & priv - > ptp_lock , flags ) ;
return 0 ;
}
static int stmmac_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
return - EOPNOTSUPP ;
}
/* structure describing a PTP hardware clock */
2017-08-21 22:36:50 +05:30
static const struct ptp_clock_info stmmac_ptp_clock_ops = {
2013-03-26 04:43:11 +00:00
. owner = THIS_MODULE ,
. name = " stmmac_ptp_clock " ,
. max_adj = 62500000 ,
. n_alarm = 0 ,
. n_ext_ts = 0 ,
. n_per_out = 0 ,
2014-03-20 22:21:55 +01:00
. n_pins = 0 ,
2013-03-26 04:43:11 +00:00
. pps = 0 ,
. adjfreq = stmmac_adjust_freq ,
. adjtime = stmmac_adjust_time ,
2015-03-29 23:12:07 +02:00
. gettime64 = stmmac_get_time ,
. settime64 = stmmac_set_time ,
2013-03-26 04:43:11 +00:00
. enable = stmmac_enable ,
} ;
/**
* stmmac_ptp_register
2013-04-08 02:10:00 +00:00
* @ priv : driver private structure
2013-03-26 04:43:11 +00:00
* Description : this function will register the ptp clock driver
* to kernel . It also does some house keeping work .
*/
2016-10-19 09:06:41 +02:00
void stmmac_ptp_register ( struct stmmac_priv * priv )
2013-03-26 04:43:11 +00:00
{
spin_lock_init ( & priv - > ptp_lock ) ;
priv - > ptp_clock_ops = stmmac_ptp_clock_ops ;
priv - > ptp_clock = ptp_clock_register ( & priv - > ptp_clock_ops ,
priv - > device ) ;
if ( IS_ERR ( priv - > ptp_clock ) ) {
2016-10-19 09:06:41 +02:00
netdev_err ( priv - > dev , " ptp_clock_register failed \n " ) ;
2013-03-26 04:43:11 +00:00
priv - > ptp_clock = NULL ;
2016-10-19 09:06:41 +02:00
} else if ( priv - > ptp_clock )
netdev_info ( priv - > dev , " registered PTP clock \n " ) ;
2013-03-26 04:43:11 +00:00
}
/**
* stmmac_ptp_unregister
2013-04-08 02:10:00 +00:00
* @ priv : driver private structure
2013-03-26 04:43:11 +00:00
* Description : this function will remove / unregister the ptp clock driver
* from the kernel .
*/
void stmmac_ptp_unregister ( struct stmmac_priv * priv )
{
if ( priv - > ptp_clock ) {
ptp_clock_unregister ( priv - > ptp_clock ) ;
2014-08-25 14:56:19 +02:00
priv - > ptp_clock = NULL ;
2013-03-26 04:43:11 +00:00
pr_debug ( " Removed PTP HW clock successfully on %s \n " ,
priv - > dev - > name ) ;
}
}