xref: /linux/drivers/ptp/ptp_kvm_common.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1a8cf291bSJianyong Wu // SPDX-License-Identifier: GPL-2.0-or-later
2a8cf291bSJianyong Wu /*
3a8cf291bSJianyong Wu  * Virtual PTP 1588 clock for use with KVM guests
4a8cf291bSJianyong Wu  *
5a8cf291bSJianyong Wu  * Copyright (C) 2017 Red Hat Inc.
6a8cf291bSJianyong Wu  */
7a8cf291bSJianyong Wu #include <linux/device.h>
8a8cf291bSJianyong Wu #include <linux/err.h>
9a8cf291bSJianyong Wu #include <linux/init.h>
10a8cf291bSJianyong Wu #include <linux/kernel.h>
11a8cf291bSJianyong Wu #include <linux/slab.h>
12a8cf291bSJianyong Wu #include <linux/module.h>
13a8cf291bSJianyong Wu #include <linux/ptp_kvm.h>
14a8cf291bSJianyong Wu #include <uapi/linux/kvm_para.h>
15a8cf291bSJianyong Wu #include <asm/kvm_para.h>
16a8cf291bSJianyong Wu #include <uapi/asm/kvm_para.h>
17a8cf291bSJianyong Wu 
18a8cf291bSJianyong Wu #include <linux/ptp_clock_kernel.h>
19a8cf291bSJianyong Wu 
20a8cf291bSJianyong Wu struct kvm_ptp_clock {
21a8cf291bSJianyong Wu 	struct ptp_clock *ptp_clock;
22a8cf291bSJianyong Wu 	struct ptp_clock_info caps;
23a8cf291bSJianyong Wu };
24a8cf291bSJianyong Wu 
25a8cf291bSJianyong Wu static DEFINE_SPINLOCK(kvm_ptp_lock);
26a8cf291bSJianyong Wu 
ptp_kvm_get_time_fn(ktime_t * device_time,struct system_counterval_t * system_counter,void * ctx)27a8cf291bSJianyong Wu static int ptp_kvm_get_time_fn(ktime_t *device_time,
28a8cf291bSJianyong Wu 			       struct system_counterval_t *system_counter,
29a8cf291bSJianyong Wu 			       void *ctx)
30a8cf291bSJianyong Wu {
319be3b2f0SPeter Hilber 	enum clocksource_ids cs_id;
32a8cf291bSJianyong Wu 	struct timespec64 tspec;
339be3b2f0SPeter Hilber 	u64 cycle;
349be3b2f0SPeter Hilber 	int ret;
35a8cf291bSJianyong Wu 
36a8cf291bSJianyong Wu 	spin_lock(&kvm_ptp_lock);
37a8cf291bSJianyong Wu 
38a8cf291bSJianyong Wu 	preempt_disable_notrace();
39*b152688cSPeter Hilber 	ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs_id);
40a8cf291bSJianyong Wu 	if (ret) {
41a8cf291bSJianyong Wu 		spin_unlock(&kvm_ptp_lock);
42a8cf291bSJianyong Wu 		preempt_enable_notrace();
43a8cf291bSJianyong Wu 		return ret;
44a8cf291bSJianyong Wu 	}
45a8cf291bSJianyong Wu 
46a8cf291bSJianyong Wu 	preempt_enable_notrace();
47a8cf291bSJianyong Wu 
48a8cf291bSJianyong Wu 	system_counter->cycles = cycle;
499be3b2f0SPeter Hilber 	system_counter->cs_id = cs_id;
50a8cf291bSJianyong Wu 
51a8cf291bSJianyong Wu 	*device_time = timespec64_to_ktime(tspec);
52a8cf291bSJianyong Wu 
53a8cf291bSJianyong Wu 	spin_unlock(&kvm_ptp_lock);
54a8cf291bSJianyong Wu 
55a8cf291bSJianyong Wu 	return 0;
56a8cf291bSJianyong Wu }
57a8cf291bSJianyong Wu 
ptp_kvm_getcrosststamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)58a8cf291bSJianyong Wu static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp,
59a8cf291bSJianyong Wu 				  struct system_device_crosststamp *xtstamp)
60a8cf291bSJianyong Wu {
61a8cf291bSJianyong Wu 	return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL,
62a8cf291bSJianyong Wu 					     NULL, xtstamp);
63a8cf291bSJianyong Wu }
64a8cf291bSJianyong Wu 
65a8cf291bSJianyong Wu /*
66a8cf291bSJianyong Wu  * PTP clock operations
67a8cf291bSJianyong Wu  */
68a8cf291bSJianyong Wu 
ptp_kvm_adjfine(struct ptp_clock_info * ptp,long delta)6973aa29a2SJacob Keller static int ptp_kvm_adjfine(struct ptp_clock_info *ptp, long delta)
70a8cf291bSJianyong Wu {
71a8cf291bSJianyong Wu 	return -EOPNOTSUPP;
72a8cf291bSJianyong Wu }
73a8cf291bSJianyong Wu 
ptp_kvm_adjtime(struct ptp_clock_info * ptp,s64 delta)74a8cf291bSJianyong Wu static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta)
75a8cf291bSJianyong Wu {
76a8cf291bSJianyong Wu 	return -EOPNOTSUPP;
77a8cf291bSJianyong Wu }
78a8cf291bSJianyong Wu 
ptp_kvm_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)79a8cf291bSJianyong Wu static int ptp_kvm_settime(struct ptp_clock_info *ptp,
80a8cf291bSJianyong Wu 			   const struct timespec64 *ts)
81a8cf291bSJianyong Wu {
82a8cf291bSJianyong Wu 	return -EOPNOTSUPP;
83a8cf291bSJianyong Wu }
84a8cf291bSJianyong Wu 
ptp_kvm_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)85a8cf291bSJianyong Wu static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
86a8cf291bSJianyong Wu {
87a8cf291bSJianyong Wu 	long ret;
88a8cf291bSJianyong Wu 	struct timespec64 tspec;
89a8cf291bSJianyong Wu 
90a8cf291bSJianyong Wu 	spin_lock(&kvm_ptp_lock);
91a8cf291bSJianyong Wu 
92a8cf291bSJianyong Wu 	ret = kvm_arch_ptp_get_clock(&tspec);
93a8cf291bSJianyong Wu 	if (ret) {
94a8cf291bSJianyong Wu 		spin_unlock(&kvm_ptp_lock);
95a8cf291bSJianyong Wu 		return ret;
96a8cf291bSJianyong Wu 	}
97a8cf291bSJianyong Wu 
98a8cf291bSJianyong Wu 	spin_unlock(&kvm_ptp_lock);
99a8cf291bSJianyong Wu 
100a8cf291bSJianyong Wu 	memcpy(ts, &tspec, sizeof(struct timespec64));
101a8cf291bSJianyong Wu 
102a8cf291bSJianyong Wu 	return 0;
103a8cf291bSJianyong Wu }
104a8cf291bSJianyong Wu 
ptp_kvm_enable(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)105a8cf291bSJianyong Wu static int ptp_kvm_enable(struct ptp_clock_info *ptp,
106a8cf291bSJianyong Wu 			  struct ptp_clock_request *rq, int on)
107a8cf291bSJianyong Wu {
108a8cf291bSJianyong Wu 	return -EOPNOTSUPP;
109a8cf291bSJianyong Wu }
110a8cf291bSJianyong Wu 
111a8cf291bSJianyong Wu static const struct ptp_clock_info ptp_kvm_caps = {
112a8cf291bSJianyong Wu 	.owner		= THIS_MODULE,
113a8cf291bSJianyong Wu 	.name		= "KVM virtual PTP",
114a8cf291bSJianyong Wu 	.max_adj	= 0,
115a8cf291bSJianyong Wu 	.n_ext_ts	= 0,
116a8cf291bSJianyong Wu 	.n_pins		= 0,
117a8cf291bSJianyong Wu 	.pps		= 0,
11873aa29a2SJacob Keller 	.adjfine	= ptp_kvm_adjfine,
119a8cf291bSJianyong Wu 	.adjtime	= ptp_kvm_adjtime,
120a8cf291bSJianyong Wu 	.gettime64	= ptp_kvm_gettime,
121a8cf291bSJianyong Wu 	.settime64	= ptp_kvm_settime,
122a8cf291bSJianyong Wu 	.enable		= ptp_kvm_enable,
123a8cf291bSJianyong Wu 	.getcrosststamp = ptp_kvm_getcrosststamp,
124a8cf291bSJianyong Wu };
125a8cf291bSJianyong Wu 
126a8cf291bSJianyong Wu /* module operations */
127a8cf291bSJianyong Wu 
128a8cf291bSJianyong Wu static struct kvm_ptp_clock kvm_ptp_clock;
129a8cf291bSJianyong Wu 
ptp_kvm_exit(void)130a8cf291bSJianyong Wu static void __exit ptp_kvm_exit(void)
131a8cf291bSJianyong Wu {
132a8cf291bSJianyong Wu 	ptp_clock_unregister(kvm_ptp_clock.ptp_clock);
1336365ba64SJeremi Piotrowski 	kvm_arch_ptp_exit();
134a8cf291bSJianyong Wu }
135a8cf291bSJianyong Wu 
ptp_kvm_init(void)136a8cf291bSJianyong Wu static int __init ptp_kvm_init(void)
137a8cf291bSJianyong Wu {
138a8cf291bSJianyong Wu 	long ret;
139a8cf291bSJianyong Wu 
140a8cf291bSJianyong Wu 	ret = kvm_arch_ptp_init();
141a8cf291bSJianyong Wu 	if (ret) {
142a86ed2cfSJon Hunter 		if (ret != -EOPNOTSUPP)
143a8cf291bSJianyong Wu 			pr_err("fail to initialize ptp_kvm");
144a8cf291bSJianyong Wu 		return ret;
145a8cf291bSJianyong Wu 	}
146a8cf291bSJianyong Wu 
147a8cf291bSJianyong Wu 	kvm_ptp_clock.caps = ptp_kvm_caps;
148a8cf291bSJianyong Wu 
149a8cf291bSJianyong Wu 	kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL);
150a8cf291bSJianyong Wu 
151a8cf291bSJianyong Wu 	return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock);
152a8cf291bSJianyong Wu }
153a8cf291bSJianyong Wu 
154a8cf291bSJianyong Wu module_init(ptp_kvm_init);
155a8cf291bSJianyong Wu module_exit(ptp_kvm_exit);
156a8cf291bSJianyong Wu 
157a8cf291bSJianyong Wu MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
158a8cf291bSJianyong Wu MODULE_DESCRIPTION("PTP clock using KVMCLOCK");
159a8cf291bSJianyong Wu MODULE_LICENSE("GPL");
160