1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Virtual PTP 1588 clock for use with KVM guests
5 * Copyright (C) 2017 Red Hat Inc.
7 #include <linux/device.h>
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <linux/ptp_kvm.h>
14 #include <uapi/linux/kvm_para.h>
15 #include <asm/kvm_para.h>
16 #include <uapi/asm/kvm_para.h>
18 #include <linux/ptp_clock_kernel.h>
20 struct kvm_ptp_clock
{
21 struct ptp_clock
*ptp_clock
;
22 struct ptp_clock_info caps
;
25 static DEFINE_SPINLOCK(kvm_ptp_lock
);
27 static int ptp_kvm_get_time_fn(ktime_t
*device_time
,
28 struct system_counterval_t
*system_counter
,
31 enum clocksource_ids cs_id
;
32 struct timespec64 tspec
;
36 spin_lock(&kvm_ptp_lock
);
38 preempt_disable_notrace();
39 ret
= kvm_arch_ptp_get_crosststamp(&cycle
, &tspec
, &cs_id
);
41 spin_unlock(&kvm_ptp_lock
);
42 preempt_enable_notrace();
46 preempt_enable_notrace();
48 system_counter
->cycles
= cycle
;
49 system_counter
->cs_id
= cs_id
;
51 *device_time
= timespec64_to_ktime(tspec
);
53 spin_unlock(&kvm_ptp_lock
);
58 static int ptp_kvm_getcrosststamp(struct ptp_clock_info
*ptp
,
59 struct system_device_crosststamp
*xtstamp
)
61 return get_device_system_crosststamp(ptp_kvm_get_time_fn
, NULL
,
66 * PTP clock operations
69 static int ptp_kvm_adjfine(struct ptp_clock_info
*ptp
, long delta
)
74 static int ptp_kvm_adjtime(struct ptp_clock_info
*ptp
, s64 delta
)
79 static int ptp_kvm_settime(struct ptp_clock_info
*ptp
,
80 const struct timespec64
*ts
)
85 static int ptp_kvm_gettime(struct ptp_clock_info
*ptp
, struct timespec64
*ts
)
88 struct timespec64 tspec
;
90 spin_lock(&kvm_ptp_lock
);
92 ret
= kvm_arch_ptp_get_clock(&tspec
);
94 spin_unlock(&kvm_ptp_lock
);
98 spin_unlock(&kvm_ptp_lock
);
100 memcpy(ts
, &tspec
, sizeof(struct timespec64
));
105 static int ptp_kvm_enable(struct ptp_clock_info
*ptp
,
106 struct ptp_clock_request
*rq
, int on
)
111 static const struct ptp_clock_info ptp_kvm_caps
= {
112 .owner
= THIS_MODULE
,
113 .name
= "KVM virtual PTP",
118 .adjfine
= ptp_kvm_adjfine
,
119 .adjtime
= ptp_kvm_adjtime
,
120 .gettime64
= ptp_kvm_gettime
,
121 .settime64
= ptp_kvm_settime
,
122 .enable
= ptp_kvm_enable
,
123 .getcrosststamp
= ptp_kvm_getcrosststamp
,
126 /* module operations */
128 static struct kvm_ptp_clock kvm_ptp_clock
;
130 static void __exit
ptp_kvm_exit(void)
132 ptp_clock_unregister(kvm_ptp_clock
.ptp_clock
);
136 static int __init
ptp_kvm_init(void)
140 ret
= kvm_arch_ptp_init();
142 if (ret
!= -EOPNOTSUPP
)
143 pr_err("fail to initialize ptp_kvm");
147 kvm_ptp_clock
.caps
= ptp_kvm_caps
;
149 kvm_ptp_clock
.ptp_clock
= ptp_clock_register(&kvm_ptp_clock
.caps
, NULL
);
151 return PTR_ERR_OR_ZERO(kvm_ptp_clock
.ptp_clock
);
154 module_init(ptp_kvm_init
);
155 module_exit(ptp_kvm_exit
);
157 MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
158 MODULE_DESCRIPTION("PTP clock using KVMCLOCK");
159 MODULE_LICENSE("GPL");