2019-10-22 09:53:22 +00:00
// SPDX-License-Identifier: GPL-2.0-only
/* Aquantia Corporation Network Driver
* Copyright ( C ) 2014 - 2019 Aquantia Corporation . All rights reserved
*/
/* File aq_ptp.c:
* Definition of functions for Linux PTP support .
*/
# include <linux/ptp_clock_kernel.h>
# include <linux/clocksource.h>
# include "aq_nic.h"
# include "aq_ptp.h"
struct aq_ptp_s {
struct aq_nic_s * aq_nic ;
2019-10-22 09:53:27 +00:00
spinlock_t ptp_lock ;
2019-10-22 09:53:22 +00:00
struct ptp_clock * ptp_clock ;
struct ptp_clock_info ptp_info ;
} ;
2019-10-22 09:53:27 +00:00
/* aq_ptp_adjfine
* @ ptp : the ptp clock structure
* @ ppb : parts per billion adjustment from base
*
* adjust the frequency of the ptp cycle counter by the
* indicated ppb from the base frequency .
*/
static int aq_ptp_adjfine ( struct ptp_clock_info * ptp , long scaled_ppm )
{
struct aq_ptp_s * aq_ptp = container_of ( ptp , struct aq_ptp_s , ptp_info ) ;
struct aq_nic_s * aq_nic = aq_ptp - > aq_nic ;
mutex_lock ( & aq_nic - > fwreq_mutex ) ;
aq_nic - > aq_hw_ops - > hw_adj_clock_freq ( aq_nic - > aq_hw ,
scaled_ppm_to_ppb ( scaled_ppm ) ) ;
mutex_unlock ( & aq_nic - > fwreq_mutex ) ;
return 0 ;
}
/* aq_ptp_adjtime
* @ ptp : the ptp clock structure
* @ delta : offset to adjust the cycle counter by
*
* adjust the timer by resetting the timecounter structure .
*/
static int aq_ptp_adjtime ( struct ptp_clock_info * ptp , s64 delta )
{
struct aq_ptp_s * aq_ptp = container_of ( ptp , struct aq_ptp_s , ptp_info ) ;
struct aq_nic_s * aq_nic = aq_ptp - > aq_nic ;
unsigned long flags ;
spin_lock_irqsave ( & aq_ptp - > ptp_lock , flags ) ;
aq_nic - > aq_hw_ops - > hw_adj_sys_clock ( aq_nic - > aq_hw , delta ) ;
spin_unlock_irqrestore ( & aq_ptp - > ptp_lock , flags ) ;
return 0 ;
}
/* aq_ptp_gettime
* @ ptp : the ptp clock structure
* @ ts : timespec structure to hold the current time value
*
* read the timecounter and return the correct value on ns ,
* after converting it into a struct timespec .
*/
static int aq_ptp_gettime ( struct ptp_clock_info * ptp , struct timespec64 * ts )
{
struct aq_ptp_s * aq_ptp = container_of ( ptp , struct aq_ptp_s , ptp_info ) ;
struct aq_nic_s * aq_nic = aq_ptp - > aq_nic ;
unsigned long flags ;
u64 ns ;
spin_lock_irqsave ( & aq_ptp - > ptp_lock , flags ) ;
aq_nic - > aq_hw_ops - > hw_get_ptp_ts ( aq_nic - > aq_hw , & ns ) ;
spin_unlock_irqrestore ( & aq_ptp - > ptp_lock , flags ) ;
* ts = ns_to_timespec64 ( ns ) ;
return 0 ;
}
/* aq_ptp_settime
* @ ptp : the ptp clock structure
* @ ts : the timespec containing the new time for the cycle counter
*
* reset the timecounter to use a new base value instead of the kernel
* wall timer value .
*/
static int aq_ptp_settime ( struct ptp_clock_info * ptp ,
const struct timespec64 * ts )
{
struct aq_ptp_s * aq_ptp = container_of ( ptp , struct aq_ptp_s , ptp_info ) ;
struct aq_nic_s * aq_nic = aq_ptp - > aq_nic ;
unsigned long flags ;
u64 ns = timespec64_to_ns ( ts ) ;
u64 now ;
spin_lock_irqsave ( & aq_ptp - > ptp_lock , flags ) ;
aq_nic - > aq_hw_ops - > hw_get_ptp_ts ( aq_nic - > aq_hw , & now ) ;
aq_nic - > aq_hw_ops - > hw_adj_sys_clock ( aq_nic - > aq_hw , ( s64 ) ns - ( s64 ) now ) ;
spin_unlock_irqrestore ( & aq_ptp - > ptp_lock , flags ) ;
return 0 ;
}
2019-10-22 09:53:22 +00:00
static struct ptp_clock_info aq_ptp_clock = {
. owner = THIS_MODULE ,
. name = " atlantic ptp " ,
2019-10-22 09:53:27 +00:00
. max_adj = 999999999 ,
2019-10-22 09:53:22 +00:00
. n_ext_ts = 0 ,
. pps = 0 ,
2019-10-22 09:53:27 +00:00
. adjfine = aq_ptp_adjfine ,
. adjtime = aq_ptp_adjtime ,
. gettime64 = aq_ptp_gettime ,
. settime64 = aq_ptp_settime ,
2019-10-22 09:53:22 +00:00
. n_per_out = 0 ,
. n_pins = 0 ,
. pin_config = NULL ,
} ;
int aq_ptp_init ( struct aq_nic_s * aq_nic , unsigned int idx_vec )
{
struct hw_atl_utils_mbox mbox ;
2019-10-22 09:53:27 +00:00
struct ptp_clock * clock ;
2019-10-22 09:53:22 +00:00
struct aq_ptp_s * aq_ptp ;
int err = 0 ;
2019-10-22 09:53:27 +00:00
if ( ! aq_nic - > aq_hw_ops - > hw_get_ptp_ts ) {
aq_nic - > aq_ptp = NULL ;
return 0 ;
}
if ( ! aq_nic - > aq_fw_ops - > enable_ptp ) {
aq_nic - > aq_ptp = NULL ;
return 0 ;
}
2019-10-22 09:53:22 +00:00
hw_atl_utils_mpi_read_stats ( aq_nic - > aq_hw , & mbox ) ;
if ( ! ( mbox . info . caps_ex & BIT ( CAPS_EX_PHY_PTP_EN ) ) ) {
aq_nic - > aq_ptp = NULL ;
return 0 ;
}
aq_ptp = kzalloc ( sizeof ( * aq_ptp ) , GFP_KERNEL ) ;
if ( ! aq_ptp ) {
err = - ENOMEM ;
goto err_exit ;
}
aq_ptp - > aq_nic = aq_nic ;
2019-10-22 09:53:27 +00:00
spin_lock_init ( & aq_ptp - > ptp_lock ) ;
2019-10-22 09:53:22 +00:00
aq_ptp - > ptp_info = aq_ptp_clock ;
2019-10-22 09:53:27 +00:00
clock = ptp_clock_register ( & aq_ptp - > ptp_info , & aq_nic - > ndev - > dev ) ;
if ( ! clock | | IS_ERR ( clock ) ) {
netdev_err ( aq_nic - > ndev , " ptp_clock_register failed \n " ) ;
err = PTR_ERR ( clock ) ;
goto err_exit ;
}
aq_ptp - > ptp_clock = clock ;
2019-10-22 09:53:22 +00:00
aq_nic - > aq_ptp = aq_ptp ;
2019-10-22 09:53:27 +00:00
/* enable ptp counter */
aq_utils_obj_set ( & aq_nic - > aq_hw - > flags , AQ_HW_PTP_AVAILABLE ) ;
mutex_lock ( & aq_nic - > fwreq_mutex ) ;
aq_nic - > aq_fw_ops - > enable_ptp ( aq_nic - > aq_hw , 1 ) ;
aq_ptp_clock_init ( aq_nic ) ;
mutex_unlock ( & aq_nic - > fwreq_mutex ) ;
2019-10-22 09:53:22 +00:00
return 0 ;
err_exit :
kfree ( aq_ptp ) ;
aq_nic - > aq_ptp = NULL ;
return err ;
}
void aq_ptp_unregister ( struct aq_nic_s * aq_nic )
{
struct aq_ptp_s * aq_ptp = aq_nic - > aq_ptp ;
if ( ! aq_ptp )
return ;
ptp_clock_unregister ( aq_ptp - > ptp_clock ) ;
}
void aq_ptp_free ( struct aq_nic_s * aq_nic )
{
struct aq_ptp_s * aq_ptp = aq_nic - > aq_ptp ;
if ( ! aq_ptp )
return ;
2019-10-22 09:53:27 +00:00
/* disable ptp */
mutex_lock ( & aq_nic - > fwreq_mutex ) ;
aq_nic - > aq_fw_ops - > enable_ptp ( aq_nic - > aq_hw , 0 ) ;
mutex_unlock ( & aq_nic - > fwreq_mutex ) ;
2019-10-22 09:53:22 +00:00
kfree ( aq_ptp ) ;
aq_nic - > aq_ptp = NULL ;
}