diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
index 6403e746eeb4..7a518185cb11 100644
--- a/Documentation/ABI/testing/sysfs-ptp
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -25,6 +25,18 @@ Description:
 		MAC based ones. The string does not necessarily have
 		to be any kind of unique id.
 
+What:		/sys/class/ptp/ptpN/domain
+Date:		May 2021
+Contact:	Yangbo Lu <yangbo.lu@nxp.com>
+Description:
+		This file contains the domain value that the PTP virtual
+		clock serves. PTP virtual clock will provide timestamp
+		to PTP messages which have same domain matched.
+		Write value 0 ~ 255 into this file to change the domain.
+		Writing -1 means serving no domain. If PTP message domain
+		value does not have a PTP virtual clock matched, the
+		original hardware time stamp will be used.
+
 What:		/sys/class/ptp/ptpN/max_adjustment
 Date:		September 2010
 Contact:	Richard Cochran <richardcochran@gmail.com>
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 9b8ab1e6625f..4ad20b2aae57 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -216,6 +216,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 	ptp->info = info;
 	ptp->devid = MKDEV(major, index);
 	ptp->index = index;
+	ptp->domain = -1;	/* No domain set */
 	spin_lock_init(&ptp->tsevq.lock);
 	mutex_init(&ptp->tsevq_mux);
 	mutex_init(&ptp->pincfg_mux);
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index da24d0c83799..fe8f976b7b75 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -48,6 +48,7 @@ struct ptp_clock {
 	struct kthread_worker *kworker;
 	struct kthread_delayed_work aux_work;
 	u8 num_vclocks;
+	int16_t domain;
 };
 
 #define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
@@ -70,6 +71,11 @@ struct ptp_vclock {
 	u32 div_factor;
 };
 
+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
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index 5e1b5947dbff..b854661aadeb 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c
@@ -223,6 +223,57 @@ static ssize_t num_vclocks_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(num_vclocks);
 
+static int check_domain_avail(struct device *dev, void *data)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	int16_t *domain = data;
+
+	if (ptp->domain == *domain)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t domain_show(struct device *dev,
+			   struct device_attribute *attr, char *page)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+
+	return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->domain);
+}
+
+static ssize_t domain_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	int err = -EINVAL;
+	int16_t domain;
+
+	if (kstrtos16(buf, 0, &domain))
+		goto out;
+
+	if (domain > 255 || domain < -1)
+		goto out;
+
+	if (domain == -1) {
+		ptp->domain = -1;
+		return count;
+	}
+
+	if (device_for_each_child(dev->parent, &domain, check_domain_avail)) {
+		dev_err(dev, "the domain value already in used\n");
+		goto out;
+	}
+
+	ptp->domain = domain;
+
+	return count;
+out:
+	return err;
+}
+static DEVICE_ATTR_RW(domain);
+
 static struct attribute *ptp_attrs[] = {
 	&dev_attr_clock_name.attr,
 
@@ -238,6 +289,7 @@ static struct attribute *ptp_attrs[] = {
 	&dev_attr_period.attr,
 	&dev_attr_pps_enable.attr,
 	&dev_attr_num_vclocks.attr,
+	&dev_attr_domain.attr,
 	NULL
 };
 
@@ -262,6 +314,9 @@ static umode_t ptp_is_attribute_visible(struct kobject *kobj,
 	} else if (attr == &dev_attr_num_vclocks.attr) {
 		if (info->vclock_flag || !info->vclock_cc)
 			mode = 0;
+	} else if (attr == &dev_attr_domain.attr) {
+		if (!info->vclock_flag)
+			mode = 0;
 	}
 
 	return mode;
diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c
index fce09f10ae69..6e7d1403d024 100644
--- a/drivers/ptp/ptp_vclock.c
+++ b/drivers/ptp/ptp_vclock.c
@@ -87,6 +87,39 @@ static void ptp_vclock_refresh(struct work_struct *work)
 	schedule_delayed_work(&vclock->refresh_work, vclock->refresh_interval);
 }
 
+static int ptp_convert_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;
+
+	/* Convert to domain tstamp if there is a domain matched */
+	if (ptp->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);
+		return -EINVAL;	/* For break. Not error. */
+	}
+
+	return 0;
+}
+
+void ptp_clock_domain_tstamp(struct device *ptp_dev, u64 *tstamp, u8 domain)
+{
+	struct domain_tstamp domain_ts;
+
+	domain_ts.tstamp = *tstamp;
+	domain_ts.domain = domain;
+
+	device_for_each_child(ptp_dev, &domain_ts, ptp_convert_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);
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 8f34f192f2cf..edc816cdfb2c 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -343,6 +343,15 @@ void ptp_cancel_worker_sync(struct ptp_clock *ptp);
  * @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 timestamp
+ *
+ * @ptp_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 *ptp_dev, u64 *tstamp, u8 domain);
 #else
 static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 						   struct device *parent)
@@ -366,6 +375,10 @@ 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; }
+
+static inline 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)
