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