162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Virtual PTP 1588 clock for use with KVM guests 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/ptp_kvm.h> 1462306a36Sopenharmony_ci#include <uapi/linux/kvm_para.h> 1562306a36Sopenharmony_ci#include <asm/kvm_para.h> 1662306a36Sopenharmony_ci#include <uapi/asm/kvm_para.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct kvm_ptp_clock { 2162306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 2262306a36Sopenharmony_ci struct ptp_clock_info caps; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(kvm_ptp_lock); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int ptp_kvm_get_time_fn(ktime_t *device_time, 2862306a36Sopenharmony_ci struct system_counterval_t *system_counter, 2962306a36Sopenharmony_ci void *ctx) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci long ret; 3262306a36Sopenharmony_ci u64 cycle; 3362306a36Sopenharmony_ci struct timespec64 tspec; 3462306a36Sopenharmony_ci struct clocksource *cs; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci spin_lock(&kvm_ptp_lock); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci preempt_disable_notrace(); 3962306a36Sopenharmony_ci ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs); 4062306a36Sopenharmony_ci if (ret) { 4162306a36Sopenharmony_ci spin_unlock(&kvm_ptp_lock); 4262306a36Sopenharmony_ci preempt_enable_notrace(); 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci preempt_enable_notrace(); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci system_counter->cycles = cycle; 4962306a36Sopenharmony_ci system_counter->cs = cs; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci *device_time = timespec64_to_ktime(tspec); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci spin_unlock(&kvm_ptp_lock); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp, 5962306a36Sopenharmony_ci struct system_device_crosststamp *xtstamp) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL, 6262306a36Sopenharmony_ci NULL, xtstamp); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * PTP clock operations 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int ptp_kvm_adjfine(struct ptp_clock_info *ptp, long delta) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return -EOPNOTSUPP; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return -EOPNOTSUPP; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int ptp_kvm_settime(struct ptp_clock_info *ptp, 8062306a36Sopenharmony_ci const struct timespec64 *ts) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci return -EOPNOTSUPP; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci long ret; 8862306a36Sopenharmony_ci struct timespec64 tspec; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci spin_lock(&kvm_ptp_lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = kvm_arch_ptp_get_clock(&tspec); 9362306a36Sopenharmony_ci if (ret) { 9462306a36Sopenharmony_ci spin_unlock(&kvm_ptp_lock); 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci spin_unlock(&kvm_ptp_lock); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci memcpy(ts, &tspec, sizeof(struct timespec64)); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int ptp_kvm_enable(struct ptp_clock_info *ptp, 10662306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return -EOPNOTSUPP; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct ptp_clock_info ptp_kvm_caps = { 11262306a36Sopenharmony_ci .owner = THIS_MODULE, 11362306a36Sopenharmony_ci .name = "KVM virtual PTP", 11462306a36Sopenharmony_ci .max_adj = 0, 11562306a36Sopenharmony_ci .n_ext_ts = 0, 11662306a36Sopenharmony_ci .n_pins = 0, 11762306a36Sopenharmony_ci .pps = 0, 11862306a36Sopenharmony_ci .adjfine = ptp_kvm_adjfine, 11962306a36Sopenharmony_ci .adjtime = ptp_kvm_adjtime, 12062306a36Sopenharmony_ci .gettime64 = ptp_kvm_gettime, 12162306a36Sopenharmony_ci .settime64 = ptp_kvm_settime, 12262306a36Sopenharmony_ci .enable = ptp_kvm_enable, 12362306a36Sopenharmony_ci .getcrosststamp = ptp_kvm_getcrosststamp, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* module operations */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct kvm_ptp_clock kvm_ptp_clock; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void __exit ptp_kvm_exit(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci ptp_clock_unregister(kvm_ptp_clock.ptp_clock); 13362306a36Sopenharmony_ci kvm_arch_ptp_exit(); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int __init ptp_kvm_init(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci long ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = kvm_arch_ptp_init(); 14162306a36Sopenharmony_ci if (ret) { 14262306a36Sopenharmony_ci if (ret != -EOPNOTSUPP) 14362306a36Sopenharmony_ci pr_err("fail to initialize ptp_kvm"); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci kvm_ptp_clock.caps = ptp_kvm_caps; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cimodule_init(ptp_kvm_init); 15562306a36Sopenharmony_cimodule_exit(ptp_kvm_exit); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciMODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); 15862306a36Sopenharmony_ciMODULE_DESCRIPTION("PTP clock using KVMCLOCK"); 15962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 160