diff options
Diffstat (limited to 'drivers/ptp/ptp_kvm_common.c')
| -rw-r--r-- | drivers/ptp/ptp_kvm_common.c | 158 | 
1 files changed, 158 insertions, 0 deletions
| diff --git a/drivers/ptp/ptp_kvm_common.c b/drivers/ptp/ptp_kvm_common.c new file mode 100644 index 000000000000..fcae32f56f25 --- /dev/null +++ b/drivers/ptp/ptp_kvm_common.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/ptp_kvm.h> +#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; +}; + +static DEFINE_SPINLOCK(kvm_ptp_lock); + +static int ptp_kvm_get_time_fn(ktime_t *device_time, +			       struct system_counterval_t *system_counter, +			       void *ctx) +{ +	long ret; +	u64 cycle; +	struct timespec64 tspec; +	struct clocksource *cs; + +	spin_lock(&kvm_ptp_lock); + +	preempt_disable_notrace(); +	ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs); +	if (ret) { +		spin_unlock(&kvm_ptp_lock); +		preempt_enable_notrace(); +		return ret; +	} + +	preempt_enable_notrace(); + +	system_counter->cycles = cycle; +	system_counter->cs = cs; + +	*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) +{ +	long ret; +	struct timespec64 tspec; + +	spin_lock(&kvm_ptp_lock); + +	ret = kvm_arch_ptp_get_clock(&tspec); +	if (ret) { +		spin_unlock(&kvm_ptp_lock); +		return ret; +	} + +	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; +} + +static const struct ptp_clock_info ptp_kvm_caps = { +	.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) +{ +	long ret; + +	ret = kvm_arch_ptp_init(); +	if (ret) { +		if (ret != -EOPNOTSUPP) +			pr_err("fail to initialize ptp_kvm"); +		return ret; +	} + +	kvm_ptp_clock.caps = ptp_kvm_caps; + +	kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); + +	return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock); +} + +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"); | 
