xref: /openbmc/linux/drivers/ptp/ptp_vclock.c (revision 67d93ffc)
15d43f951SYangbo Lu // SPDX-License-Identifier: GPL-2.0-or-later
25d43f951SYangbo Lu /*
35d43f951SYangbo Lu  * PTP virtual clock driver
45d43f951SYangbo Lu  *
55d43f951SYangbo Lu  * Copyright 2021 NXP
65d43f951SYangbo Lu  */
75d43f951SYangbo Lu #include <linux/slab.h>
8fcf308e5SGerhard Engleder #include <linux/hashtable.h>
95d43f951SYangbo Lu #include "ptp_private.h"
105d43f951SYangbo Lu 
115d43f951SYangbo Lu #define PTP_VCLOCK_CC_SHIFT		31
125d43f951SYangbo Lu #define PTP_VCLOCK_CC_MULT		(1 << PTP_VCLOCK_CC_SHIFT)
135d43f951SYangbo Lu #define PTP_VCLOCK_FADJ_SHIFT		9
145d43f951SYangbo Lu #define PTP_VCLOCK_FADJ_DENOMINATOR	15625ULL
155d43f951SYangbo Lu #define PTP_VCLOCK_REFRESH_INTERVAL	(HZ * 2)
165d43f951SYangbo Lu 
17fcf308e5SGerhard Engleder /* protects vclock_hash addition/deletion */
18fcf308e5SGerhard Engleder static DEFINE_SPINLOCK(vclock_hash_lock);
19fcf308e5SGerhard Engleder 
20fcf308e5SGerhard Engleder static DEFINE_READ_MOSTLY_HASHTABLE(vclock_hash, 8);
21fcf308e5SGerhard Engleder 
ptp_vclock_hash_add(struct ptp_vclock * vclock)22fcf308e5SGerhard Engleder static void ptp_vclock_hash_add(struct ptp_vclock *vclock)
23fcf308e5SGerhard Engleder {
24fcf308e5SGerhard Engleder 	spin_lock(&vclock_hash_lock);
25fcf308e5SGerhard Engleder 
26fcf308e5SGerhard Engleder 	hlist_add_head_rcu(&vclock->vclock_hash_node,
27fcf308e5SGerhard Engleder 			   &vclock_hash[vclock->clock->index % HASH_SIZE(vclock_hash)]);
28fcf308e5SGerhard Engleder 
29fcf308e5SGerhard Engleder 	spin_unlock(&vclock_hash_lock);
30fcf308e5SGerhard Engleder }
31fcf308e5SGerhard Engleder 
ptp_vclock_hash_del(struct ptp_vclock * vclock)32fcf308e5SGerhard Engleder static void ptp_vclock_hash_del(struct ptp_vclock *vclock)
33fcf308e5SGerhard Engleder {
34fcf308e5SGerhard Engleder 	spin_lock(&vclock_hash_lock);
35fcf308e5SGerhard Engleder 
36fcf308e5SGerhard Engleder 	hlist_del_init_rcu(&vclock->vclock_hash_node);
37fcf308e5SGerhard Engleder 
38fcf308e5SGerhard Engleder 	spin_unlock(&vclock_hash_lock);
39fcf308e5SGerhard Engleder 
40fcf308e5SGerhard Engleder 	synchronize_rcu();
41fcf308e5SGerhard Engleder }
42fcf308e5SGerhard Engleder 
ptp_vclock_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)435d43f951SYangbo Lu static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
445d43f951SYangbo Lu {
455d43f951SYangbo Lu 	struct ptp_vclock *vclock = info_to_vclock(ptp);
465d43f951SYangbo Lu 	s64 adj;
475d43f951SYangbo Lu 
485d43f951SYangbo Lu 	adj = (s64)scaled_ppm << PTP_VCLOCK_FADJ_SHIFT;
495d43f951SYangbo Lu 	adj = div_s64(adj, PTP_VCLOCK_FADJ_DENOMINATOR);
505d43f951SYangbo Lu 
51*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
52*67d93ffcSÍñigo Huguet 		return -EINTR;
535d43f951SYangbo Lu 	timecounter_read(&vclock->tc);
545d43f951SYangbo Lu 	vclock->cc.mult = PTP_VCLOCK_CC_MULT + adj;
55*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
565d43f951SYangbo Lu 
575d43f951SYangbo Lu 	return 0;
585d43f951SYangbo Lu }
595d43f951SYangbo Lu 
ptp_vclock_adjtime(struct ptp_clock_info * ptp,s64 delta)605d43f951SYangbo Lu static int ptp_vclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
615d43f951SYangbo Lu {
625d43f951SYangbo Lu 	struct ptp_vclock *vclock = info_to_vclock(ptp);
635d43f951SYangbo Lu 
64*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
65*67d93ffcSÍñigo Huguet 		return -EINTR;
665d43f951SYangbo Lu 	timecounter_adjtime(&vclock->tc, delta);
67*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
685d43f951SYangbo Lu 
695d43f951SYangbo Lu 	return 0;
705d43f951SYangbo Lu }
715d43f951SYangbo Lu 
ptp_vclock_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)725d43f951SYangbo Lu static int ptp_vclock_gettime(struct ptp_clock_info *ptp,
735d43f951SYangbo Lu 			      struct timespec64 *ts)
745d43f951SYangbo Lu {
755d43f951SYangbo Lu 	struct ptp_vclock *vclock = info_to_vclock(ptp);
765d43f951SYangbo Lu 	u64 ns;
775d43f951SYangbo Lu 
78*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
79*67d93ffcSÍñigo Huguet 		return -EINTR;
805d43f951SYangbo Lu 	ns = timecounter_read(&vclock->tc);
81*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
825d43f951SYangbo Lu 	*ts = ns_to_timespec64(ns);
835d43f951SYangbo Lu 
845d43f951SYangbo Lu 	return 0;
855d43f951SYangbo Lu }
865d43f951SYangbo Lu 
ptp_vclock_gettimex(struct ptp_clock_info * ptp,struct timespec64 * ts,struct ptp_system_timestamp * sts)87f0067ebfSMiroslav Lichvar static int ptp_vclock_gettimex(struct ptp_clock_info *ptp,
88f0067ebfSMiroslav Lichvar 			       struct timespec64 *ts,
89f0067ebfSMiroslav Lichvar 			       struct ptp_system_timestamp *sts)
90f0067ebfSMiroslav Lichvar {
91f0067ebfSMiroslav Lichvar 	struct ptp_vclock *vclock = info_to_vclock(ptp);
92f0067ebfSMiroslav Lichvar 	struct ptp_clock *pptp = vclock->pclock;
93f0067ebfSMiroslav Lichvar 	struct timespec64 pts;
94f0067ebfSMiroslav Lichvar 	int err;
95f0067ebfSMiroslav Lichvar 	u64 ns;
96f0067ebfSMiroslav Lichvar 
9742704b26SGerhard Engleder 	err = pptp->info->getcyclesx64(pptp->info, &pts, sts);
98f0067ebfSMiroslav Lichvar 	if (err)
99f0067ebfSMiroslav Lichvar 		return err;
100f0067ebfSMiroslav Lichvar 
101*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
102*67d93ffcSÍñigo Huguet 		return -EINTR;
103f0067ebfSMiroslav Lichvar 	ns = timecounter_cyc2time(&vclock->tc, timespec64_to_ns(&pts));
104*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
105f0067ebfSMiroslav Lichvar 
106f0067ebfSMiroslav Lichvar 	*ts = ns_to_timespec64(ns);
107f0067ebfSMiroslav Lichvar 
108f0067ebfSMiroslav Lichvar 	return 0;
109f0067ebfSMiroslav Lichvar }
110f0067ebfSMiroslav Lichvar 
ptp_vclock_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)1115d43f951SYangbo Lu static int ptp_vclock_settime(struct ptp_clock_info *ptp,
1125d43f951SYangbo Lu 			      const struct timespec64 *ts)
1135d43f951SYangbo Lu {
1145d43f951SYangbo Lu 	struct ptp_vclock *vclock = info_to_vclock(ptp);
1155d43f951SYangbo Lu 	u64 ns = timespec64_to_ns(ts);
1165d43f951SYangbo Lu 
117*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
118*67d93ffcSÍñigo Huguet 		return -EINTR;
1195d43f951SYangbo Lu 	timecounter_init(&vclock->tc, &vclock->cc, ns);
120*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
1215d43f951SYangbo Lu 
1225d43f951SYangbo Lu 	return 0;
1235d43f951SYangbo Lu }
1245d43f951SYangbo Lu 
ptp_vclock_getcrosststamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)12521fad630SMiroslav Lichvar static int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp,
12621fad630SMiroslav Lichvar 				     struct system_device_crosststamp *xtstamp)
12721fad630SMiroslav Lichvar {
12821fad630SMiroslav Lichvar 	struct ptp_vclock *vclock = info_to_vclock(ptp);
12921fad630SMiroslav Lichvar 	struct ptp_clock *pptp = vclock->pclock;
13021fad630SMiroslav Lichvar 	int err;
13121fad630SMiroslav Lichvar 	u64 ns;
13221fad630SMiroslav Lichvar 
13342704b26SGerhard Engleder 	err = pptp->info->getcrosscycles(pptp->info, xtstamp);
13421fad630SMiroslav Lichvar 	if (err)
13521fad630SMiroslav Lichvar 		return err;
13621fad630SMiroslav Lichvar 
137*67d93ffcSÍñigo Huguet 	if (mutex_lock_interruptible(&vclock->lock))
138*67d93ffcSÍñigo Huguet 		return -EINTR;
13921fad630SMiroslav Lichvar 	ns = timecounter_cyc2time(&vclock->tc, ktime_to_ns(xtstamp->device));
140*67d93ffcSÍñigo Huguet 	mutex_unlock(&vclock->lock);
14121fad630SMiroslav Lichvar 
14221fad630SMiroslav Lichvar 	xtstamp->device = ns_to_ktime(ns);
14321fad630SMiroslav Lichvar 
14421fad630SMiroslav Lichvar 	return 0;
14521fad630SMiroslav Lichvar }
14621fad630SMiroslav Lichvar 
ptp_vclock_refresh(struct ptp_clock_info * ptp)1475d43f951SYangbo Lu static long ptp_vclock_refresh(struct ptp_clock_info *ptp)
1485d43f951SYangbo Lu {
1495d43f951SYangbo Lu 	struct ptp_vclock *vclock = info_to_vclock(ptp);
1505d43f951SYangbo Lu 	struct timespec64 ts;
1515d43f951SYangbo Lu 
1525d43f951SYangbo Lu 	ptp_vclock_gettime(&vclock->info, &ts);
1535d43f951SYangbo Lu 
1545d43f951SYangbo Lu 	return PTP_VCLOCK_REFRESH_INTERVAL;
1555d43f951SYangbo Lu }
1565d43f951SYangbo Lu 
1575d43f951SYangbo Lu static const struct ptp_clock_info ptp_vclock_info = {
1585d43f951SYangbo Lu 	.owner		= THIS_MODULE,
1595d43f951SYangbo Lu 	.name		= "ptp virtual clock",
160f77222d6SMiroslav Lichvar 	.max_adj	= 500000000,
1615d43f951SYangbo Lu 	.adjfine	= ptp_vclock_adjfine,
1625d43f951SYangbo Lu 	.adjtime	= ptp_vclock_adjtime,
1635d43f951SYangbo Lu 	.settime64	= ptp_vclock_settime,
1645d43f951SYangbo Lu 	.do_aux_work	= ptp_vclock_refresh,
1655d43f951SYangbo Lu };
1665d43f951SYangbo Lu 
ptp_vclock_read(const struct cyclecounter * cc)1675d43f951SYangbo Lu static u64 ptp_vclock_read(const struct cyclecounter *cc)
1685d43f951SYangbo Lu {
1695d43f951SYangbo Lu 	struct ptp_vclock *vclock = cc_to_vclock(cc);
1705d43f951SYangbo Lu 	struct ptp_clock *ptp = vclock->pclock;
1715d43f951SYangbo Lu 	struct timespec64 ts = {};
1725d43f951SYangbo Lu 
17342704b26SGerhard Engleder 	ptp->info->getcycles64(ptp->info, &ts);
1745d43f951SYangbo Lu 
1755d43f951SYangbo Lu 	return timespec64_to_ns(&ts);
1765d43f951SYangbo Lu }
1775d43f951SYangbo Lu 
1785d43f951SYangbo Lu static const struct cyclecounter ptp_vclock_cc = {
1795d43f951SYangbo Lu 	.read	= ptp_vclock_read,
1805d43f951SYangbo Lu 	.mask	= CYCLECOUNTER_MASK(32),
1815d43f951SYangbo Lu 	.mult	= PTP_VCLOCK_CC_MULT,
1825d43f951SYangbo Lu 	.shift	= PTP_VCLOCK_CC_SHIFT,
1835d43f951SYangbo Lu };
1845d43f951SYangbo Lu 
ptp_vclock_register(struct ptp_clock * pclock)1855d43f951SYangbo Lu struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
1865d43f951SYangbo Lu {
1875d43f951SYangbo Lu 	struct ptp_vclock *vclock;
1885d43f951SYangbo Lu 
1895d43f951SYangbo Lu 	vclock = kzalloc(sizeof(*vclock), GFP_KERNEL);
1905d43f951SYangbo Lu 	if (!vclock)
1915d43f951SYangbo Lu 		return NULL;
1925d43f951SYangbo Lu 
1935d43f951SYangbo Lu 	vclock->pclock = pclock;
1945d43f951SYangbo Lu 	vclock->info = ptp_vclock_info;
19542704b26SGerhard Engleder 	if (pclock->info->getcyclesx64)
196f0067ebfSMiroslav Lichvar 		vclock->info.gettimex64 = ptp_vclock_gettimex;
197f0067ebfSMiroslav Lichvar 	else
198f0067ebfSMiroslav Lichvar 		vclock->info.gettime64 = ptp_vclock_gettime;
19942704b26SGerhard Engleder 	if (pclock->info->getcrosscycles)
20021fad630SMiroslav Lichvar 		vclock->info.getcrosststamp = ptp_vclock_getcrosststamp;
2015d43f951SYangbo Lu 	vclock->cc = ptp_vclock_cc;
2025d43f951SYangbo Lu 
2035d43f951SYangbo Lu 	snprintf(vclock->info.name, PTP_CLOCK_NAME_LEN, "ptp%d_virt",
2045d43f951SYangbo Lu 		 pclock->index);
2055d43f951SYangbo Lu 
206fcf308e5SGerhard Engleder 	INIT_HLIST_NODE(&vclock->vclock_hash_node);
207fcf308e5SGerhard Engleder 
208*67d93ffcSÍñigo Huguet 	mutex_init(&vclock->lock);
2095d43f951SYangbo Lu 
2105d43f951SYangbo Lu 	vclock->clock = ptp_clock_register(&vclock->info, &pclock->dev);
2115d43f951SYangbo Lu 	if (IS_ERR_OR_NULL(vclock->clock)) {
2125d43f951SYangbo Lu 		kfree(vclock);
2135d43f951SYangbo Lu 		return NULL;
2145d43f951SYangbo Lu 	}
2155d43f951SYangbo Lu 
2165d43f951SYangbo Lu 	timecounter_init(&vclock->tc, &vclock->cc, 0);
2175d43f951SYangbo Lu 	ptp_schedule_worker(vclock->clock, PTP_VCLOCK_REFRESH_INTERVAL);
2185d43f951SYangbo Lu 
219fcf308e5SGerhard Engleder 	ptp_vclock_hash_add(vclock);
220fcf308e5SGerhard Engleder 
2215d43f951SYangbo Lu 	return vclock;
2225d43f951SYangbo Lu }
2235d43f951SYangbo Lu 
ptp_vclock_unregister(struct ptp_vclock * vclock)2245d43f951SYangbo Lu void ptp_vclock_unregister(struct ptp_vclock *vclock)
2255d43f951SYangbo Lu {
226fcf308e5SGerhard Engleder 	ptp_vclock_hash_del(vclock);
227fcf308e5SGerhard Engleder 
2285d43f951SYangbo Lu 	ptp_clock_unregister(vclock->clock);
2295d43f951SYangbo Lu 	kfree(vclock);
2305d43f951SYangbo Lu }
231acb288e8SYangbo Lu 
232e5f31552SArnd Bergmann #if IS_BUILTIN(CONFIG_PTP_1588_CLOCK)
ptp_get_vclocks_index(int pclock_index,int ** vclock_index)233acb288e8SYangbo Lu int ptp_get_vclocks_index(int pclock_index, int **vclock_index)
234acb288e8SYangbo Lu {
235acb288e8SYangbo Lu 	char name[PTP_CLOCK_NAME_LEN] = "";
236acb288e8SYangbo Lu 	struct ptp_clock *ptp;
237acb288e8SYangbo Lu 	struct device *dev;
238acb288e8SYangbo Lu 	int num = 0;
239acb288e8SYangbo Lu 
240acb288e8SYangbo Lu 	if (pclock_index < 0)
241acb288e8SYangbo Lu 		return num;
242acb288e8SYangbo Lu 
243acb288e8SYangbo Lu 	snprintf(name, PTP_CLOCK_NAME_LEN, "ptp%d", pclock_index);
244acb288e8SYangbo Lu 	dev = class_find_device_by_name(ptp_class, name);
245acb288e8SYangbo Lu 	if (!dev)
246acb288e8SYangbo Lu 		return num;
247acb288e8SYangbo Lu 
248acb288e8SYangbo Lu 	ptp = dev_get_drvdata(dev);
249acb288e8SYangbo Lu 
250acb288e8SYangbo Lu 	if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) {
251acb288e8SYangbo Lu 		put_device(dev);
252acb288e8SYangbo Lu 		return num;
253acb288e8SYangbo Lu 	}
254acb288e8SYangbo Lu 
255acb288e8SYangbo Lu 	*vclock_index = kzalloc(sizeof(int) * ptp->n_vclocks, GFP_KERNEL);
256acb288e8SYangbo Lu 	if (!(*vclock_index))
257acb288e8SYangbo Lu 		goto out;
258acb288e8SYangbo Lu 
259acb288e8SYangbo Lu 	memcpy(*vclock_index, ptp->vclock_index, sizeof(int) * ptp->n_vclocks);
260acb288e8SYangbo Lu 	num = ptp->n_vclocks;
261acb288e8SYangbo Lu out:
262acb288e8SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
263acb288e8SYangbo Lu 	put_device(dev);
264acb288e8SYangbo Lu 	return num;
265acb288e8SYangbo Lu }
266acb288e8SYangbo Lu EXPORT_SYMBOL(ptp_get_vclocks_index);
267895487a3SYangbo Lu 
ptp_convert_timestamp(const ktime_t * hwtstamp,int vclock_index)268d58809d8SGerhard Engleder ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index)
269895487a3SYangbo Lu {
270fcf308e5SGerhard Engleder 	unsigned int hash = vclock_index % HASH_SIZE(vclock_hash);
271895487a3SYangbo Lu 	struct ptp_vclock *vclock;
272895487a3SYangbo Lu 	u64 ns;
273fcf308e5SGerhard Engleder 	u64 vclock_ns = 0;
274895487a3SYangbo Lu 
275d58809d8SGerhard Engleder 	ns = ktime_to_ns(*hwtstamp);
276895487a3SYangbo Lu 
277fcf308e5SGerhard Engleder 	rcu_read_lock();
278895487a3SYangbo Lu 
279fcf308e5SGerhard Engleder 	hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) {
280fcf308e5SGerhard Engleder 		if (vclock->clock->index != vclock_index)
281fcf308e5SGerhard Engleder 			continue;
282fcf308e5SGerhard Engleder 
283*67d93ffcSÍñigo Huguet 		if (mutex_lock_interruptible(&vclock->lock))
284*67d93ffcSÍñigo Huguet 			break;
285fcf308e5SGerhard Engleder 		vclock_ns = timecounter_cyc2time(&vclock->tc, ns);
286*67d93ffcSÍñigo Huguet 		mutex_unlock(&vclock->lock);
287fcf308e5SGerhard Engleder 		break;
288fcf308e5SGerhard Engleder 	}
289fcf308e5SGerhard Engleder 
290fcf308e5SGerhard Engleder 	rcu_read_unlock();
291fcf308e5SGerhard Engleder 
292fcf308e5SGerhard Engleder 	return ns_to_ktime(vclock_ns);
293895487a3SYangbo Lu }
294895487a3SYangbo Lu EXPORT_SYMBOL(ptp_convert_timestamp);
295e5f31552SArnd Bergmann #endif
296