2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-01-24 15:09:42 -02:00
/*
* Virtual PTP 1588 clock for use with KVM guests
*
* Copyright ( C ) 2017 Red Hat Inc .
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
2020-12-09 14:09:26 +08:00
# include <linux/slab.h>
2017-01-24 15:09:42 -02:00
# include <linux/module.h>
2020-12-09 14:09:26 +08:00
# include <linux/ptp_kvm.h>
2017-01-24 15:09:42 -02:00
# include <uapi/linux/kvm_para.h>
# include <asm/kvm_para.h>
# include <uapi/asm/kvm_para.h>
# include <linux/ptp_clock_kernel.h>
struct kvm_ptp_clock {
struct ptp_clock * ptp_clock ;
struct ptp_clock_info caps ;
} ;
2020-04-18 09:51:54 +08:00
static DEFINE_SPINLOCK ( kvm_ptp_lock ) ;
2017-01-24 15:09:42 -02:00
static int ptp_kvm_get_time_fn ( ktime_t * device_time ,
struct system_counterval_t * system_counter ,
void * ctx )
{
2020-12-09 14:09:26 +08:00
long ret ;
u64 cycle ;
2017-01-24 15:09:42 -02:00
struct timespec64 tspec ;
2020-12-09 14:09:26 +08:00
struct clocksource * cs ;
2017-01-24 15:09:42 -02:00
spin_lock ( & kvm_ptp_lock ) ;
preempt_disable_notrace ( ) ;
2020-12-09 14:09:26 +08:00
ret = kvm_arch_ptp_get_crosststamp ( & cycle , & tspec , & cs ) ;
if ( ret ) {
spin_unlock ( & kvm_ptp_lock ) ;
preempt_enable_notrace ( ) ;
return ret ;
}
2017-01-24 15:09:42 -02:00
preempt_enable_notrace ( ) ;
2020-12-09 14:09:26 +08:00
system_counter - > cycles = cycle ;
system_counter - > cs = cs ;
2017-01-24 15:09:42 -02:00
* device_time = timespec64_to_ktime ( tspec ) ;
spin_unlock ( & kvm_ptp_lock ) ;
return 0 ;
}
static int ptp_kvm_getcrosststamp ( struct ptp_clock_info * ptp ,
struct system_device_crosststamp * xtstamp )
{
return get_device_system_crosststamp ( ptp_kvm_get_time_fn , NULL ,
NULL , xtstamp ) ;
}
/*
* PTP clock operations
*/
static int ptp_kvm_adjfreq ( struct ptp_clock_info * ptp , s32 ppb )
{
return - EOPNOTSUPP ;
}
static int ptp_kvm_adjtime ( struct ptp_clock_info * ptp , s64 delta )
{
return - EOPNOTSUPP ;
}
static int ptp_kvm_settime ( struct ptp_clock_info * ptp ,
const struct timespec64 * ts )
{
return - EOPNOTSUPP ;
}
static int ptp_kvm_gettime ( struct ptp_clock_info * ptp , struct timespec64 * ts )
{
2020-12-09 14:09:26 +08:00
long ret ;
2017-01-24 15:09:42 -02:00
struct timespec64 tspec ;
spin_lock ( & kvm_ptp_lock ) ;
2020-12-09 14:09:26 +08:00
ret = kvm_arch_ptp_get_clock ( & tspec ) ;
if ( ret ) {
2017-01-24 15:09:42 -02:00
spin_unlock ( & kvm_ptp_lock ) ;
2020-12-09 14:09:26 +08:00
return ret ;
2017-01-24 15:09:42 -02:00
}
spin_unlock ( & kvm_ptp_lock ) ;
memcpy ( ts , & tspec , sizeof ( struct timespec64 ) ) ;
return 0 ;
}
static int ptp_kvm_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
return - EOPNOTSUPP ;
}
2017-08-21 23:01:12 +05:30
static const struct ptp_clock_info ptp_kvm_caps = {
2017-01-24 15:09:42 -02:00
. owner = THIS_MODULE ,
. name = " KVM virtual PTP " ,
. max_adj = 0 ,
. n_ext_ts = 0 ,
. n_pins = 0 ,
. pps = 0 ,
. adjfreq = ptp_kvm_adjfreq ,
. adjtime = ptp_kvm_adjtime ,
. gettime64 = ptp_kvm_gettime ,
. settime64 = ptp_kvm_settime ,
. enable = ptp_kvm_enable ,
. getcrosststamp = ptp_kvm_getcrosststamp ,
} ;
/* module operations */
static struct kvm_ptp_clock kvm_ptp_clock ;
static void __exit ptp_kvm_exit ( void )
{
ptp_clock_unregister ( kvm_ptp_clock . ptp_clock ) ;
}
static int __init ptp_kvm_init ( void )
{
2017-02-15 20:27:20 +01:00
long ret ;
2020-12-09 14:09:26 +08:00
ret = kvm_arch_ptp_init ( ) ;
if ( ret ) {
2021-04-20 14:24:19 +01:00
if ( ret ! = - EOPNOTSUPP )
pr_err ( " fail to initialize ptp_kvm " ) ;
2020-12-09 14:09:26 +08:00
return ret ;
}
2017-02-15 20:27:20 +01:00
2017-01-24 15:09:42 -02:00
kvm_ptp_clock . caps = ptp_kvm_caps ;
kvm_ptp_clock . ptp_clock = ptp_clock_register ( & kvm_ptp_clock . caps , NULL ) ;
2017-01-28 05:01:51 +08:00
return PTR_ERR_OR_ZERO ( kvm_ptp_clock . ptp_clock ) ;
2017-01-24 15:09:42 -02:00
}
module_init ( ptp_kvm_init ) ;
module_exit ( ptp_kvm_exit ) ;
MODULE_AUTHOR ( " Marcelo Tosatti <mtosatti@redhat.com> " ) ;
MODULE_DESCRIPTION ( " PTP clock using KVMCLOCK " ) ;
MODULE_LICENSE ( " GPL " ) ;