162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PTP virtual clock driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2021 NXP
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/hashtable.h>
962306a36Sopenharmony_ci#include "ptp_private.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define PTP_VCLOCK_CC_SHIFT		31
1262306a36Sopenharmony_ci#define PTP_VCLOCK_CC_MULT		(1 << PTP_VCLOCK_CC_SHIFT)
1362306a36Sopenharmony_ci#define PTP_VCLOCK_FADJ_SHIFT		9
1462306a36Sopenharmony_ci#define PTP_VCLOCK_FADJ_DENOMINATOR	15625ULL
1562306a36Sopenharmony_ci#define PTP_VCLOCK_REFRESH_INTERVAL	(HZ * 2)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* protects vclock_hash addition/deletion */
1862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vclock_hash_lock);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic DEFINE_READ_MOSTLY_HASHTABLE(vclock_hash, 8);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic void ptp_vclock_hash_add(struct ptp_vclock *vclock)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	spin_lock(&vclock_hash_lock);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	hlist_add_head_rcu(&vclock->vclock_hash_node,
2762306a36Sopenharmony_ci			   &vclock_hash[vclock->clock->index % HASH_SIZE(vclock_hash)]);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	spin_unlock(&vclock_hash_lock);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void ptp_vclock_hash_del(struct ptp_vclock *vclock)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	spin_lock(&vclock_hash_lock);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	hlist_del_init_rcu(&vclock->vclock_hash_node);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	spin_unlock(&vclock_hash_lock);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	synchronize_rcu();
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
4662306a36Sopenharmony_ci	s64 adj;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	adj = (s64)scaled_ppm << PTP_VCLOCK_FADJ_SHIFT;
4962306a36Sopenharmony_ci	adj = div_s64(adj, PTP_VCLOCK_FADJ_DENOMINATOR);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
5262306a36Sopenharmony_ci		return -EINTR;
5362306a36Sopenharmony_ci	timecounter_read(&vclock->tc);
5462306a36Sopenharmony_ci	vclock->cc.mult = PTP_VCLOCK_CC_MULT + adj;
5562306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return 0;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int ptp_vclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
6562306a36Sopenharmony_ci		return -EINTR;
6662306a36Sopenharmony_ci	timecounter_adjtime(&vclock->tc, delta);
6762306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int ptp_vclock_gettime(struct ptp_clock_info *ptp,
7362306a36Sopenharmony_ci			      struct timespec64 *ts)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
7662306a36Sopenharmony_ci	u64 ns;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
7962306a36Sopenharmony_ci		return -EINTR;
8062306a36Sopenharmony_ci	ns = timecounter_read(&vclock->tc);
8162306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
8262306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int ptp_vclock_gettimex(struct ptp_clock_info *ptp,
8862306a36Sopenharmony_ci			       struct timespec64 *ts,
8962306a36Sopenharmony_ci			       struct ptp_system_timestamp *sts)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
9262306a36Sopenharmony_ci	struct ptp_clock *pptp = vclock->pclock;
9362306a36Sopenharmony_ci	struct timespec64 pts;
9462306a36Sopenharmony_ci	int err;
9562306a36Sopenharmony_ci	u64 ns;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	err = pptp->info->getcyclesx64(pptp->info, &pts, sts);
9862306a36Sopenharmony_ci	if (err)
9962306a36Sopenharmony_ci		return err;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
10262306a36Sopenharmony_ci		return -EINTR;
10362306a36Sopenharmony_ci	ns = timecounter_cyc2time(&vclock->tc, timespec64_to_ns(&pts));
10462306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int ptp_vclock_settime(struct ptp_clock_info *ptp,
11262306a36Sopenharmony_ci			      const struct timespec64 *ts)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
11562306a36Sopenharmony_ci	u64 ns = timespec64_to_ns(ts);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
11862306a36Sopenharmony_ci		return -EINTR;
11962306a36Sopenharmony_ci	timecounter_init(&vclock->tc, &vclock->cc, ns);
12062306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp,
12662306a36Sopenharmony_ci				     struct system_device_crosststamp *xtstamp)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
12962306a36Sopenharmony_ci	struct ptp_clock *pptp = vclock->pclock;
13062306a36Sopenharmony_ci	int err;
13162306a36Sopenharmony_ci	u64 ns;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	err = pptp->info->getcrosscycles(pptp->info, xtstamp);
13462306a36Sopenharmony_ci	if (err)
13562306a36Sopenharmony_ci		return err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (mutex_lock_interruptible(&vclock->lock))
13862306a36Sopenharmony_ci		return -EINTR;
13962306a36Sopenharmony_ci	ns = timecounter_cyc2time(&vclock->tc, ktime_to_ns(xtstamp->device));
14062306a36Sopenharmony_ci	mutex_unlock(&vclock->lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	xtstamp->device = ns_to_ktime(ns);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic long ptp_vclock_refresh(struct ptp_clock_info *ptp)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct ptp_vclock *vclock = info_to_vclock(ptp);
15062306a36Sopenharmony_ci	struct timespec64 ts;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ptp_vclock_gettime(&vclock->info, &ts);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return PTP_VCLOCK_REFRESH_INTERVAL;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic const struct ptp_clock_info ptp_vclock_info = {
15862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
15962306a36Sopenharmony_ci	.name		= "ptp virtual clock",
16062306a36Sopenharmony_ci	.max_adj	= 500000000,
16162306a36Sopenharmony_ci	.adjfine	= ptp_vclock_adjfine,
16262306a36Sopenharmony_ci	.adjtime	= ptp_vclock_adjtime,
16362306a36Sopenharmony_ci	.settime64	= ptp_vclock_settime,
16462306a36Sopenharmony_ci	.do_aux_work	= ptp_vclock_refresh,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic u64 ptp_vclock_read(const struct cyclecounter *cc)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct ptp_vclock *vclock = cc_to_vclock(cc);
17062306a36Sopenharmony_ci	struct ptp_clock *ptp = vclock->pclock;
17162306a36Sopenharmony_ci	struct timespec64 ts = {};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ptp->info->getcycles64(ptp->info, &ts);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return timespec64_to_ns(&ts);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct cyclecounter ptp_vclock_cc = {
17962306a36Sopenharmony_ci	.read	= ptp_vclock_read,
18062306a36Sopenharmony_ci	.mask	= CYCLECOUNTER_MASK(32),
18162306a36Sopenharmony_ci	.mult	= PTP_VCLOCK_CC_MULT,
18262306a36Sopenharmony_ci	.shift	= PTP_VCLOCK_CC_SHIFT,
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistruct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct ptp_vclock *vclock;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	vclock = kzalloc(sizeof(*vclock), GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!vclock)
19162306a36Sopenharmony_ci		return NULL;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	vclock->pclock = pclock;
19462306a36Sopenharmony_ci	vclock->info = ptp_vclock_info;
19562306a36Sopenharmony_ci	if (pclock->info->getcyclesx64)
19662306a36Sopenharmony_ci		vclock->info.gettimex64 = ptp_vclock_gettimex;
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		vclock->info.gettime64 = ptp_vclock_gettime;
19962306a36Sopenharmony_ci	if (pclock->info->getcrosscycles)
20062306a36Sopenharmony_ci		vclock->info.getcrosststamp = ptp_vclock_getcrosststamp;
20162306a36Sopenharmony_ci	vclock->cc = ptp_vclock_cc;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	snprintf(vclock->info.name, PTP_CLOCK_NAME_LEN, "ptp%d_virt",
20462306a36Sopenharmony_ci		 pclock->index);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	INIT_HLIST_NODE(&vclock->vclock_hash_node);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	mutex_init(&vclock->lock);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	vclock->clock = ptp_clock_register(&vclock->info, &pclock->dev);
21162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(vclock->clock)) {
21262306a36Sopenharmony_ci		kfree(vclock);
21362306a36Sopenharmony_ci		return NULL;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	timecounter_init(&vclock->tc, &vclock->cc, 0);
21762306a36Sopenharmony_ci	ptp_schedule_worker(vclock->clock, PTP_VCLOCK_REFRESH_INTERVAL);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ptp_vclock_hash_add(vclock);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return vclock;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_civoid ptp_vclock_unregister(struct ptp_vclock *vclock)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	ptp_vclock_hash_del(vclock);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ptp_clock_unregister(vclock->clock);
22962306a36Sopenharmony_ci	kfree(vclock);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_PTP_1588_CLOCK)
23362306a36Sopenharmony_ciint ptp_get_vclocks_index(int pclock_index, int **vclock_index)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	char name[PTP_CLOCK_NAME_LEN] = "";
23662306a36Sopenharmony_ci	struct ptp_clock *ptp;
23762306a36Sopenharmony_ci	struct device *dev;
23862306a36Sopenharmony_ci	int num = 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (pclock_index < 0)
24162306a36Sopenharmony_ci		return num;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	snprintf(name, PTP_CLOCK_NAME_LEN, "ptp%d", pclock_index);
24462306a36Sopenharmony_ci	dev = class_find_device_by_name(ptp_class, name);
24562306a36Sopenharmony_ci	if (!dev)
24662306a36Sopenharmony_ci		return num;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ptp = dev_get_drvdata(dev);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) {
25162306a36Sopenharmony_ci		put_device(dev);
25262306a36Sopenharmony_ci		return num;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	*vclock_index = kzalloc(sizeof(int) * ptp->n_vclocks, GFP_KERNEL);
25662306a36Sopenharmony_ci	if (!(*vclock_index))
25762306a36Sopenharmony_ci		goto out;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	memcpy(*vclock_index, ptp->vclock_index, sizeof(int) * ptp->n_vclocks);
26062306a36Sopenharmony_ci	num = ptp->n_vclocks;
26162306a36Sopenharmony_ciout:
26262306a36Sopenharmony_ci	mutex_unlock(&ptp->n_vclocks_mux);
26362306a36Sopenharmony_ci	put_device(dev);
26462306a36Sopenharmony_ci	return num;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ciEXPORT_SYMBOL(ptp_get_vclocks_index);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ciktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	unsigned int hash = vclock_index % HASH_SIZE(vclock_hash);
27162306a36Sopenharmony_ci	struct ptp_vclock *vclock;
27262306a36Sopenharmony_ci	u64 ns;
27362306a36Sopenharmony_ci	u64 vclock_ns = 0;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	ns = ktime_to_ns(*hwtstamp);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	rcu_read_lock();
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) {
28062306a36Sopenharmony_ci		if (vclock->clock->index != vclock_index)
28162306a36Sopenharmony_ci			continue;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		if (mutex_lock_interruptible(&vclock->lock))
28462306a36Sopenharmony_ci			break;
28562306a36Sopenharmony_ci		vclock_ns = timecounter_cyc2time(&vclock->tc, ns);
28662306a36Sopenharmony_ci		mutex_unlock(&vclock->lock);
28762306a36Sopenharmony_ci		break;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	rcu_read_unlock();
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return ns_to_ktime(vclock_ns);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ciEXPORT_SYMBOL(ptp_convert_timestamp);
29562306a36Sopenharmony_ci#endif
296