diff mbox series

[net-next,1/6] ptp: add ptp virtual clock driver framework

Message ID 20210507085756.20427-2-yangbo.lu@nxp.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series ptp: support virtual clocks for multiple domains | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers success CCed 3 of 3 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit fail Errors and warnings before: 437 this patch: 125
netdev/kdoc fail Errors and warnings before: 5 this patch: 8
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns
netdev/build_allmodconfig_warn fail Errors and warnings before: 491 this patch: 97
netdev/header_inline success Link

Commit Message

Yangbo Lu May 7, 2021, 8:57 a.m. UTC
This patch is to add ptp virtual clock driver framework. The main purpose
is to support PTP multiple domains over multiple PTP virtual clocks with
only single physical clock.

What has the patch exported,

- ptp_vclock_cc structure for specifying cyclecounter information that ptp
  virtual clocks will use. When registering ptp clock for physical clock,
  the ptp virtual clock will be valid to register if the pointer of
  ptp_vclock_cc structure is provided in ptp_clock_info of physical clock.

- ptp_vclock_register/ptp_vclock_unregister APIs for ptp virtual clock
  register/unregister for specified domain number. They are private for ptp
  driver.

- ptp_clock_domain_tstamp API to convert hardware time stamp to domain time
  stamp.

- ptp_get_pclock_info API for device driver to get ptp_clock_info pointer
  of physical clock from cyclecounter pointer of ptp virtual clock.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
---
 MAINTAINERS                      |   6 ++
 drivers/ptp/Makefile             |   2 +-
 drivers/ptp/ptp_private.h        |  25 +++++
 drivers/ptp/ptp_vclock.c         | 176 +++++++++++++++++++++++++++++++
 include/linux/ptp_clock_kernel.h |  51 +++++++++
 5 files changed, 259 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ptp/ptp_vclock.c

Comments

kernel test robot May 7, 2021, 10:42 a.m. UTC | #1
Hi Yangbo,

I love your patch! Yet something to improve:

[auto build test ERROR on 9d31d2338950293ec19d9b095fbaa9030899dcb4]

url:    https://github.com/0day-ci/linux/commits/Yangbo-Lu/ptp-support-virtual-clocks-for-multiple-domains/20210507-164927
base:   9d31d2338950293ec19d9b095fbaa9030899dcb4
config: microblaze-randconfig-s032-20210507 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.3-341-g8af24329-dirty
        # https://github.com/0day-ci/linux/commit/1f46e22fa0f24ac9acde10ca897266e0bac0f367
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Yangbo-Lu/ptp-support-virtual-clocks-for-multiple-domains/20210507-164927
        git checkout 1f46e22fa0f24ac9acde10ca897266e0bac0f367
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=microblaze 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/spi/spi.h:17,
                    from include/linux/iio/common/st_sensors.h:14,
                    from include/linux/iio/common/st_sensors_i2c.h:14,
                    from drivers/iio/common/st_sensors/st_sensors_i2c.c:16:
>> include/linux/ptp_clock_kernel.h:71:22: error: field 'cc' has incomplete type
      71 |  struct cyclecounter cc;
         |                      ^~
>> include/linux/ptp_clock_kernel.h:356:1: error: expected identifier or '(' before '{' token
     356 | { return NULL; }
         | ^
>> include/linux/ptp_clock_kernel.h:358:6: warning: no previous prototype for 'ptp_clock_domain_tstamp' [-Wmissing-prototypes]
     358 | void ptp_clock_domain_tstamp(struct device *dev, u64 *tstamp, u8 domain)
         |      ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/ptp_clock_kernel.h:355:38: warning: 'ptp_get_pclock_info' declared 'static' but never defined [-Wunused-function]
     355 | static inline struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc);
         |                                      ^~~~~~~~~~~~~~~~~~~


vim +/cc +71 include/linux/ptp_clock_kernel.h

    50	
    51	/**
    52	 * struct ptp_vclock_cc - ptp virtual clock cycle counter info
    53	 *
    54	 * @cc:               cyclecounter structure
    55	 * @refresh_interval: time interval to refresh time counter, to avoid 64-bit
    56	 *                    overflow during delta conversion. For example, with
    57	 *                    cc.mult value 2^28,  there are 36 bits left of cycle
    58	 *                    counter. With 1 ns counter resolution, the overflow time
    59	 *                    is 2^36 ns which is 68.7 s. The refresh_interval may be
    60	 *                    (60 * HZ) less than 68.7 s.
    61	 * @mult_num:         parameter for cc.mult adjustment calculation, see below
    62	 * @mult_dem:         parameter for cc.mult adjustment calculation, see below
    63	 *
    64	 * scaled_ppm to adjustment(mult_adj) of cc.mult
    65	 *
    66	 * mult_adj = mult * (ppb / 10^9)
    67	 *          = mult * (scaled_ppm * 1000 / 2^16) / 10^9
    68	 *          = scaled_ppm * mult_num / mult_dem
    69	 */
    70	struct ptp_vclock_cc {
  > 71		struct cyclecounter cc;
    72		unsigned long refresh_interval;
    73		u32 mult_num;
    74		u32 mult_dem;
    75	};
    76	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot May 7, 2021, 11:26 a.m. UTC | #2
Hi Yangbo,

I love your patch! Yet something to improve:

[auto build test ERROR on 9d31d2338950293ec19d9b095fbaa9030899dcb4]

url:    https://github.com/0day-ci/linux/commits/Yangbo-Lu/ptp-support-virtual-clocks-for-multiple-domains/20210507-164927
base:   9d31d2338950293ec19d9b095fbaa9030899dcb4
config: nds32-randconfig-r012-20210507 (attached as .config)
compiler: nds32le-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/1f46e22fa0f24ac9acde10ca897266e0bac0f367
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Yangbo-Lu/ptp-support-virtual-clocks-for-multiple-domains/20210507-164927
        git checkout 1f46e22fa0f24ac9acde10ca897266e0bac0f367
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=nds32 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from drivers/ptp/ptp_private.h:16,
                    from drivers/ptp/ptp_clock.c:20:
>> include/linux/ptp_clock_kernel.h:71:22: error: field 'cc' has incomplete type
      71 |  struct cyclecounter cc;
         |                      ^~
   In file included from drivers/ptp/ptp_clock.c:20:
>> drivers/ptp/ptp_private.h:59:22: error: field 'cc' has incomplete type
      59 |  struct cyclecounter cc;
         |                      ^~
>> drivers/ptp/ptp_private.h:60:21: error: field 'tc' has incomplete type
      60 |  struct timecounter tc;
         |                     ^~
--
   In file included from drivers/ptp/ptp_private.h:16,
                    from drivers/ptp/ptp_vclock.c:8:
>> include/linux/ptp_clock_kernel.h:71:22: error: field 'cc' has incomplete type
      71 |  struct cyclecounter cc;
         |                      ^~
   In file included from drivers/ptp/ptp_vclock.c:8:
>> drivers/ptp/ptp_private.h:59:22: error: field 'cc' has incomplete type
      59 |  struct cyclecounter cc;
         |                      ^~
>> drivers/ptp/ptp_private.h:60:21: error: field 'tc' has incomplete type
      60 |  struct timecounter tc;
         |                     ^~
   drivers/ptp/ptp_vclock.c: In function 'ptp_vclock_adjfine':
>> drivers/ptp/ptp_vclock.c:20:2: error: implicit declaration of function 'timecounter_read'; did you mean 'refcount_read'? [-Werror=implicit-function-declaration]
      20 |  timecounter_read(&vclock->tc);
         |  ^~~~~~~~~~~~~~~~
         |  refcount_read
   drivers/ptp/ptp_vclock.c: In function 'ptp_vclock_adjtime':
>> drivers/ptp/ptp_vclock.c:33:2: error: implicit declaration of function 'timecounter_adjtime' [-Werror=implicit-function-declaration]
      33 |  timecounter_adjtime(&vclock->tc, delta);
         |  ^~~~~~~~~~~~~~~~~~~
   drivers/ptp/ptp_vclock.c: In function 'ptp_vclock_settime':
>> drivers/ptp/ptp_vclock.c:62:2: error: implicit declaration of function 'timecounter_init'; did you mean 'timerqueue_init'? [-Werror=implicit-function-declaration]
      62 |  timecounter_init(&vclock->tc, &vclock->cc, ns);
         |  ^~~~~~~~~~~~~~~~
         |  timerqueue_init
   drivers/ptp/ptp_vclock.c: In function 'ptp_clock_find_domain_tstamp':
>> drivers/ptp/ptp_vclock.c:103:23: error: implicit declaration of function 'timecounter_cyc2time' [-Werror=implicit-function-declaration]
     103 |   domain_ts->tstamp = timecounter_cyc2time(&vclock->tc, domain_ts->tstamp);
         |                       ^~~~~~~~~~~~~~~~~~~~
   In file included from <command-line>:
   drivers/ptp/ptp_vclock.c: In function 'ptp_get_pclock_info':
>> include/linux/kernel.h:709:32: error: dereferencing pointer to incomplete type 'const struct cyclecounter'
     709 |  BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
         |                                ^~~~~~
   include/linux/compiler_types.h:308:9: note: in definition of macro '__compiletime_assert'
     308 |   if (!(condition))     \
         |         ^~~~~~~~~
   include/linux/compiler_types.h:328:2: note: in expansion of macro '_compiletime_assert'
     328 |  _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |  ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert'
      39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
         |                                     ^~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:709:2: note: in expansion of macro 'BUILD_BUG_ON_MSG'
     709 |  BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
         |  ^~~~~~~~~~~~~~~~
   include/linux/kernel.h:709:20: note: in expansion of macro '__same_type'
     709 |  BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
         |                    ^~~~~~~~~~~
   drivers/ptp/ptp_private.h:51:25: note: in expansion of macro 'container_of'
      51 | #define cc_to_vclock(d) container_of((d), struct ptp_vclock, cc)
         |                         ^~~~~~~~~~~~
   drivers/ptp/ptp_vclock.c:126:30: note: in expansion of macro 'cc_to_vclock'
     126 |  struct ptp_vclock *vclock = cc_to_vclock(cc);
         |                              ^~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from include/linux/spi/spi.h:17,
                    from drivers/iio/accel/adxl372.c:14:
>> include/linux/ptp_clock_kernel.h:71:22: error: field 'cc' has incomplete type
      71 |  struct cyclecounter cc;
         |                      ^~


vim +/cc +71 include/linux/ptp_clock_kernel.h

    50	
    51	/**
    52	 * struct ptp_vclock_cc - ptp virtual clock cycle counter info
    53	 *
    54	 * @cc:               cyclecounter structure
    55	 * @refresh_interval: time interval to refresh time counter, to avoid 64-bit
    56	 *                    overflow during delta conversion. For example, with
    57	 *                    cc.mult value 2^28,  there are 36 bits left of cycle
    58	 *                    counter. With 1 ns counter resolution, the overflow time
    59	 *                    is 2^36 ns which is 68.7 s. The refresh_interval may be
    60	 *                    (60 * HZ) less than 68.7 s.
    61	 * @mult_num:         parameter for cc.mult adjustment calculation, see below
    62	 * @mult_dem:         parameter for cc.mult adjustment calculation, see below
    63	 *
    64	 * scaled_ppm to adjustment(mult_adj) of cc.mult
    65	 *
    66	 * mult_adj = mult * (ppb / 10^9)
    67	 *          = mult * (scaled_ppm * 1000 / 2^16) / 10^9
    68	 *          = scaled_ppm * mult_num / mult_dem
    69	 */
    70	struct ptp_vclock_cc {
  > 71		struct cyclecounter cc;
    72		unsigned long refresh_interval;
    73		u32 mult_num;
    74		u32 mult_dem;
    75	};
    76	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 4796ccf9f871..9f9280b29e47 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14733,6 +14733,12 @@  F:	drivers/net/phy/dp83640*
 F:	drivers/ptp/*
 F:	include/linux/ptp_cl*
 
+PTP VIRTUAL CLOCK SUPPORT
+M:	Yangbo Lu <yangbo.lu@nxp.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/ptp/ptp_vclock.c
+
 PTRACE SUPPORT
 M:	Oleg Nesterov <oleg@redhat.com>
 S:	Maintained
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index db5aef3bddc6..3c75d7de7793 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -3,7 +3,7 @@ 
 # Makefile for PTP 1588 clock support.
 #
 
-ptp-y					:= ptp_clock.o ptp_chardev.o ptp_sysfs.o
+ptp-y					:= ptp_clock.o ptp_chardev.o ptp_sysfs.o ptp_vclock.o
 obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp.o
 obj-$(CONFIG_PTP_1588_CLOCK_DTE)	+= ptp_dte.o
 obj-$(CONFIG_PTP_1588_CLOCK_INES)	+= ptp_ines.o
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index 6b97155148f1..9ff0afc57a7f 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -48,6 +48,29 @@  struct ptp_clock {
 	struct kthread_delayed_work aux_work;
 };
 
+#define cc_to_vclock(d) container_of((d), struct ptp_vclock, cc)
+#define dw_to_vclock(d) container_of((d), struct ptp_vclock, refresh_work)
+#define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
+
+struct ptp_vclock {
+	struct ptp_clock *pclock;
+	struct ptp_clock_info info;
+	struct ptp_clock *clock;
+	struct cyclecounter cc;
+	struct timecounter tc;
+	spinlock_t lock;	/* protects tc/cc */
+	struct delayed_work refresh_work;
+	unsigned long refresh_interval;
+	u32 mult;
+	u32 mult_num;
+	u32 mult_dem;
+};
+
+struct domain_tstamp {
+	u64 tstamp;
+	u8 domain;
+};
+
 /*
  * The function queue_cnt() is safe for readers to call without
  * holding q->lock. Readers use this function to verify that the queue
@@ -89,4 +112,6 @@  extern const struct attribute_group *ptp_groups[];
 int ptp_populate_pin_groups(struct ptp_clock *ptp);
 void ptp_cleanup_pin_groups(struct ptp_clock *ptp);
 
+struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock, u8 domain);
+void ptp_vclock_unregister(struct ptp_vclock *vclock);
 #endif
diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c
new file mode 100644
index 000000000000..765acc0f7576
--- /dev/null
+++ b/drivers/ptp/ptp_vclock.c
@@ -0,0 +1,176 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PTP virtual clock driver
+ *
+ * Copyright 2021 NXP
+ */
+#include <linux/slab.h>
+#include "ptp_private.h"
+
+static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+	struct ptp_vclock *vclock = info_to_vclock(ptp);
+	unsigned long flags;
+	s64 mult_adj;
+
+	mult_adj = (s64)scaled_ppm * vclock->mult_num;
+	mult_adj = div_s64(mult_adj, vclock->mult_dem);
+
+	spin_lock_irqsave(&vclock->lock, flags);
+	timecounter_read(&vclock->tc);
+	vclock->cc.mult = vclock->mult + mult_adj;
+	spin_unlock_irqrestore(&vclock->lock, flags);
+
+	return 0;
+}
+
+static int ptp_vclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct ptp_vclock *vclock = info_to_vclock(ptp);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vclock->lock, flags);
+	timecounter_adjtime(&vclock->tc, delta);
+	spin_unlock_irqrestore(&vclock->lock, flags);
+
+	return 0;
+}
+
+static int ptp_vclock_gettime(struct ptp_clock_info *ptp,
+			      struct timespec64 *ts)
+{
+	struct ptp_vclock *vclock = info_to_vclock(ptp);
+	unsigned long flags;
+	u64 ns;
+
+	spin_lock_irqsave(&vclock->lock, flags);
+	ns = timecounter_read(&vclock->tc);
+	spin_unlock_irqrestore(&vclock->lock, flags);
+	*ts = ns_to_timespec64(ns);
+
+	return 0;
+}
+
+static int ptp_vclock_settime(struct ptp_clock_info *ptp,
+			      const struct timespec64 *ts)
+{
+	struct ptp_vclock *vclock = info_to_vclock(ptp);
+	u64 ns = timespec64_to_ns(ts);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vclock->lock, flags);
+	timecounter_init(&vclock->tc, &vclock->cc, ns);
+	spin_unlock_irqrestore(&vclock->lock, flags);
+
+	return 0;
+}
+
+static const struct ptp_clock_info ptp_vclock_info = {
+	.owner		= THIS_MODULE,
+	.name		= "ptp vclock",
+	.adjfine	= ptp_vclock_adjfine,
+	.adjtime	= ptp_vclock_adjtime,
+	.gettime64	= ptp_vclock_gettime,
+	.settime64	= ptp_vclock_settime,
+	.max_adj	= 32000000,
+};
+
+static void ptp_vclock_refresh(struct work_struct *work)
+{
+	struct delayed_work *dw = to_delayed_work(work);
+	struct ptp_vclock *vclock = dw_to_vclock(dw);
+	struct timespec64 ts;
+
+	ptp_vclock_gettime(&vclock->info, &ts);
+	schedule_delayed_work(&vclock->refresh_work, vclock->refresh_interval);
+}
+
+static int ptp_clock_find_domain_tstamp(struct device *dev, void *data)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	struct ptp_clock_info *info = ptp->info;
+	struct domain_tstamp *domain_ts = data;
+	struct ptp_vclock *vclock;
+	unsigned long flags;
+
+	if (!info->is_vclock)
+		return 0;
+
+	/* Convert to domain tstamp if there is a domain matched */
+	if (info->domain == domain_ts->domain) {
+		vclock = info_to_vclock(info);
+		spin_lock_irqsave(&vclock->lock, flags);
+		domain_ts->tstamp = timecounter_cyc2time(&vclock->tc, domain_ts->tstamp);
+		spin_unlock_irqrestore(&vclock->lock, flags);
+		/* For break. Not error. */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void ptp_clock_domain_tstamp(struct device *dev, u64 *tstamp, u8 domain)
+{
+	struct domain_tstamp domain_ts;
+
+	domain_ts.tstamp = *tstamp;
+	domain_ts.domain = domain;
+
+	device_for_each_child(dev, &domain_ts, ptp_clock_find_domain_tstamp);
+	*tstamp = domain_ts.tstamp;
+}
+EXPORT_SYMBOL(ptp_clock_domain_tstamp);
+
+struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc)
+{
+	struct ptp_vclock *vclock = cc_to_vclock(cc);
+
+	return vclock->pclock->info;
+}
+EXPORT_SYMBOL(ptp_get_pclock_info);
+
+struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock, u8 domain)
+{
+	struct ptp_vclock_cc *vclock_cc = pclock->info->vclock_cc;
+	struct ptp_vclock *vclock;
+
+	vclock = kzalloc(sizeof(*vclock), GFP_KERNEL);
+	if (!vclock)
+		return NULL;
+
+	vclock->pclock = pclock;
+
+	vclock->info = ptp_vclock_info;
+	vclock->info.vclock_cc = vclock_cc;
+	vclock->info.is_vclock = true;
+	vclock->info.domain = domain;
+
+	vclock->cc = vclock_cc->cc;
+	vclock->mult = vclock_cc->cc.mult;
+	vclock->refresh_interval = vclock_cc->refresh_interval;
+	vclock->mult_num = vclock_cc->mult_num;
+	vclock->mult_dem = vclock_cc->mult_dem;
+
+	spin_lock_init(&vclock->lock);
+
+	vclock->clock = ptp_clock_register(&vclock->info, pclock->dev.parent);
+	if (IS_ERR_OR_NULL(vclock->clock)) {
+		kfree(vclock);
+		return NULL;
+	}
+
+	timecounter_init(&vclock->tc, &vclock->cc,
+			 ktime_to_ns(ktime_get_real()));
+
+	INIT_DELAYED_WORK(&vclock->refresh_work, ptp_vclock_refresh);
+	schedule_delayed_work(&vclock->refresh_work, vclock->refresh_interval);
+
+	return vclock;
+}
+
+void ptp_vclock_unregister(struct ptp_vclock *vclock)
+{
+	cancel_delayed_work_sync(&vclock->refresh_work);
+	ptp_clock_unregister(vclock->clock);
+	kfree(vclock);
+}
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 0d47fd33b228..b2823af9b150 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -48,6 +48,32 @@  struct ptp_system_timestamp {
 	struct timespec64 post_ts;
 };
 
+/**
+ * struct ptp_vclock_cc - ptp virtual clock cycle counter info
+ *
+ * @cc:               cyclecounter structure
+ * @refresh_interval: time interval to refresh time counter, to avoid 64-bit
+ *                    overflow during delta conversion. For example, with
+ *                    cc.mult value 2^28,  there are 36 bits left of cycle
+ *                    counter. With 1 ns counter resolution, the overflow time
+ *                    is 2^36 ns which is 68.7 s. The refresh_interval may be
+ *                    (60 * HZ) less than 68.7 s.
+ * @mult_num:         parameter for cc.mult adjustment calculation, see below
+ * @mult_dem:         parameter for cc.mult adjustment calculation, see below
+ *
+ * scaled_ppm to adjustment(mult_adj) of cc.mult
+ *
+ * mult_adj = mult * (ppb / 10^9)
+ *          = mult * (scaled_ppm * 1000 / 2^16) / 10^9
+ *          = scaled_ppm * mult_num / mult_dem
+ */
+struct ptp_vclock_cc {
+	struct cyclecounter cc;
+	unsigned long refresh_interval;
+	u32 mult_num;
+	u32 mult_dem;
+};
+
 /**
  * struct ptp_clock_info - describes a PTP hardware clock
  *
@@ -157,6 +183,11 @@  struct ptp_clock_info {
 	int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,
 		      enum ptp_pin_function func, unsigned int chan);
 	long (*do_aux_work)(struct ptp_clock_info *ptp);
+
+	/* For virtual clock */
+	struct ptp_vclock_cc *vclock_cc;
+	u8 domain;
+	bool is_vclock;
 };
 
 struct ptp_clock;
@@ -286,6 +317,21 @@  int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay);
  */
 void ptp_cancel_worker_sync(struct ptp_clock *ptp);
 
+/**
+ * ptp_get_pclock_info() - get ptp_clock_info pointer of physical clock
+ *
+ * @cc:     cyclecounter pointer of ptp virtual clock.
+ */
+struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc);
+
+/**
+ * ptp_clock_domain_tstamp() - convert to domain time stamp
+ *
+ * @dev:     device pointer of current ptp clock.
+ * @tstamp:  time stamp pointer to hardware time stamp
+ * @domain:  domain number to convert
+ */
+void ptp_clock_domain_tstamp(struct device *dev, u64 *tstamp, u8 domain);
 #else
 static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 						   struct device *parent)
@@ -306,6 +352,11 @@  static inline int ptp_schedule_worker(struct ptp_clock *ptp,
 static inline void ptp_cancel_worker_sync(struct ptp_clock *ptp)
 { }
 
+static inline struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc);
+{ return NULL; }
+
+void ptp_clock_domain_tstamp(struct device *dev, u64 *tstamp, u8 domain)
+{ }
 #endif
 
 static inline void ptp_read_system_prets(struct ptp_system_timestamp *sts)