diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index ea96a14d72d1..c87062a5a568 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -165,8 +165,10 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
 	struct ptp_sys_offset_precise precise_offset;
 	struct system_device_crosststamp xtstamp;
 	struct ptp_clock_info *ops = ptp->info;
+	struct ptp_sys_offset_stat stat_offset;
 	struct ptp_sys_offset *sysoff = NULL;
 	struct timestamp_event_queue *tsevq;
+	struct ptp_stat_extra stat_extra;
 	struct ptp_system_timestamp sts;
 	struct ptp_clock_request req;
 	struct ptp_clock_caps caps;
@@ -195,6 +197,7 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
 				    ptp->info->getmaxphase != NULL;
 		if (caps.adjust_phase)
 			caps.max_phase_adj = ptp->info->getmaxphase(ptp->info);
+		caps.stat_timestamping = ptp->info->getstattstamp != NULL;
 		if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
 			err = -EFAULT;
 		break;
@@ -347,6 +350,42 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
 			err = -EFAULT;
 		break;
 
+	case PTP_SYS_OFFSET_STAT:
+		if (!ptp->info->getstattstamp) {
+			err = -EOPNOTSUPP;
+			break;
+		}
+
+		memset(&stat_extra, 0, sizeof(stat_extra));
+
+		err = ptp->info->getstattstamp(ptp->info, &xtstamp,
+					       &stat_extra);
+		if (err)
+			break;
+
+		memset(&stat_offset, 0, sizeof(stat_offset));
+
+		ts = ktime_to_timespec64(xtstamp.device);
+		stat_offset.device.sec = ts.tv_sec;
+		stat_offset.device.nsec = ts.tv_nsec;
+		ts = ktime_to_timespec64(xtstamp.sys_realtime);
+		stat_offset.sys_realtime.sec = ts.tv_sec;
+		stat_offset.sys_realtime.nsec = ts.tv_nsec;
+		ts = ktime_to_timespec64(xtstamp.sys_monoraw);
+		stat_offset.sys_monoraw.sec = ts.tv_sec;
+		stat_offset.sys_monoraw.nsec = ts.tv_nsec;
+
+		stat_offset.accuracy = stat_extra.accuracy;
+		stat_offset.flags = stat_extra.flags;
+		stat_offset.leap = stat_extra.leap;
+		stat_offset.tai_offset_sec = stat_extra.tai_offset_sec;
+		stat_offset.smear_offset_nsec = stat_extra.smear_offset_nsec;
+
+		if (copy_to_user((void __user *)arg, &stat_offset,
+				 sizeof(stat_offset)))
+			err = -EFAULT;
+		break;
+
 	case PTP_SYS_OFFSET_EXTENDED:
 	case PTP_SYS_OFFSET_EXTENDED2:
 		if (!ptp->info->gettimex64) {
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index b932425ddc6a..3b08d133c2bb 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -209,6 +209,12 @@ static void ptp_clock_release(struct device *dev)
 	kfree(ptp);
 }
 
+static int ptp_getcrosststamp_from_stat(struct ptp_clock_info *info,
+					struct system_device_crosststamp *cts)
+{
+	return info->getstattstamp(info, cts, NULL);
+}
+
 static int ptp_getcycles64(struct ptp_clock_info *info, struct timespec64 *ts)
 {
 	if (info->getcyclesx64)
@@ -279,6 +285,9 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 	mutex_init(&ptp->n_vclocks_mux);
 	init_waitqueue_head(&ptp->tsev_wq);
 
+	if (ptp->info->getstattstamp && !ptp->info->getcrosststamp)
+		ptp->info->getcrosststamp = ptp_getcrosststamp_from_stat;
+
 	if (ptp->info->getcycles64 || ptp->info->getcyclesx64) {
 		ptp->has_cycles = true;
 		if (!ptp->info->getcycles64 && ptp->info->getcyclesx64)
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 0d68d09bedd1..9b08cbd747ae 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -55,6 +55,25 @@ struct ptp_system_timestamp {
 	clockid_t clockid;
 };
 
+/**
+ * struct ptp_stat_extra - extra clock status
+ *
+ * @accuracy:          Accuracy of the clock at the time of reading.
+ * @flags:             Same flags as struct ptp_sys_offset_stat.
+ * @leap:              One of the ptp_utc_leap enumeration values.
+ * @tai_offset_sec:    Offset of TAI from UTC at the time of clock reading,
+ *                     in the form TAI - UTC.
+ * @smear_offset_nsec: Offset of smeared clock from UTC at the time of
+ *                     clock reading, in the form UTC - smeared clock.
+ */
+struct ptp_stat_extra {
+	struct ptp_clock_accuracy accuracy;
+	unsigned int flags;
+	unsigned int leap;
+	int tai_offset_sec;
+	int smear_offset_nsec;
+};
+
 /**
  * struct ptp_clock_info - describes a PTP hardware clock
  *
@@ -109,6 +128,15 @@ struct ptp_system_timestamp {
  *                   parameter cts: Contains timestamp (device,system) pair,
  *                   where system time is realtime and monotonic.
  *
+ * @getstattstamp:  Reads the current time from the hardware clock and system
+ *                  clock simultaneously, optionally with extra status
+ *                  information.
+ *                  parameter cts: Contains timestamp (device,system) pair,
+ *                  where system time is realtime and monotonic.
+ *                  parameter stat_extra: If not NULL, it holds extra status
+ *                  information. Drivers indicate information validity through
+ *                  flags.
+ *
  * @settime64:  Set the current time on the hardware clock.
  *              parameter ts: Time value to set.
  *
@@ -184,6 +212,9 @@ struct ptp_clock_info {
 			  struct ptp_system_timestamp *sts);
 	int (*getcrosststamp)(struct ptp_clock_info *ptp,
 			      struct system_device_crosststamp *cts);
+	int (*getstattstamp)(struct ptp_clock_info *ptp,
+			     struct system_device_crosststamp *cts,
+			     struct ptp_stat_extra *stat_extra);
 	int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts);
 	int (*getcycles64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
 	int (*getcyclesx64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index 18eefa6d93d6..c26f48967b41 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -103,7 +103,12 @@ struct ptp_clock_caps {
 	/* Whether the clock supports adjust phase */
 	int adjust_phase;
 	int max_phase_adj; /* Maximum phase adjustment in nanoseconds. */
-	int rsv[11];       /* Reserved for future use. */
+	/*
+	 * Whether the clock supports precise system-device cross timestamps
+	 * with extra status information.
+	 */
+	int stat_timestamping;
+	int rsv[10];       /* Reserved for future use. */
 };
 
 struct ptp_extts_request {
@@ -184,6 +189,128 @@ struct ptp_sys_offset_precise {
 	unsigned int rsv[4];    /* Reserved for future use. */
 };
 
+/*
+ * Bits of the ptp_clock_accuracy.flags field:
+ */
+#define PTP_CLOCK_FREQ_EST_VALID (1 << 0)
+#define PTP_CLOCK_FREQ_MAX_VALID (1 << 1)
+#define PTP_CLOCK_TIME_EST_VALID (1 << 2)
+#define PTP_CLOCK_TIME_MAX_VALID (1 << 3)
+
+enum ptp_clock_status {
+	PTP_CLOCK_STATUS_UNKNOWN,
+	PTP_CLOCK_STATUS_INITIALIZING,
+	PTP_CLOCK_STATUS_SYNCHRONIZED,
+	PTP_CLOCK_STATUS_FREERUNNING,
+	PTP_CLOCK_STATUS_UNRELIABLE,
+};
+
+struct ptp_clock_accuracy {
+	/*
+	 * Estimated (absolute) drift rate, in ppm, with a 44-bit fractional
+	 * part. Field value 1 corresponds to 2^-44 ppm.
+	 *
+	 * Valid if flag PTP_CLOCK_FREQ_EST_VALID is set.
+	 */
+	unsigned long long freq_esterror;
+	/*
+	 * Maximum (absolute) drift rate, in ppm, with a 44-bit fractional
+	 * part.
+	 *
+	 * Valid if flag PTP_CLOCK_FREQ_MAX_VALID is set.
+	 */
+	unsigned long long freq_maxerror;
+	/*
+	 * Estimate of absolute value of offset from reference, in nsec.
+	 *
+	 * Valid if flag PTP_CLOCK_TIME_EST_VALID is set.
+	 */
+	unsigned long long time_esterror;
+	/*
+	 * Maximum of absolute value of offset from reference, in nsec.
+	 *
+	 * Valid if flag PTP_CLOCK_TIME_MAX_VALID is set.
+	 */
+	unsigned long long time_maxerror;
+	/* PTP_CLOCK_(FREQ|TIME)_*_VALID flags. */
+	unsigned int flags;
+	/* PTP_CLOCK_STATUS_* enum value. */
+	unsigned int clock_status;
+};
+
+/*
+ * Bits of the ptp_sys_offset_stat.flags field:
+ */
+#define PTP_CLOCK_LEAP_VALID         (1 << 0)
+#define PTP_CLOCK_TAI_OFFSET_VALID   (1 << 1)
+#define PTP_CLOCK_SMEAR_OFFSET_VALID (1 << 2)
+
+enum ptp_utc_leap {
+	/* No leap second in the present, near future, or near past. */
+	PTP_LEAP_NONE,
+	/* Positive leap second happens in the near future. */
+	PTP_LEAP_PRE_POS,
+	/* Negative leap second happens in the near future. */
+	PTP_LEAP_PRE_NEG,
+	/* Positive leap second is in progress. */
+	PTP_LEAP_POS,
+	/* Positive leap second happened recently. */
+	PTP_LEAP_POST_POS,
+	/* Negative leap second happened recently. */
+	PTP_LEAP_POST_NEG,
+	/*
+	 * Positive leap second will be smeared into the clock in the near
+	 * future.
+	 */
+	PTP_LEAP_SMEAR_PRE_POS,
+	/*
+	 * Negative leap second will be smeared into the clock in the near
+	 * future.
+	 */
+	PTP_LEAP_SMEAR_PRE_NEG,
+	/* Positive leap second is being smeared into the clock. */
+	PTP_LEAP_SMEAR_POS,
+	/* Negative leap second is being smeared into the clock. */
+	PTP_LEAP_SMEAR_NEG,
+	/* Positive leap second was smeared into the clock recently. */
+	PTP_LEAP_SMEAR_POST_POS,
+	/* Negative leap second was smeared into the clock recently. */
+	PTP_LEAP_SMEAR_POST_NEG,
+};
+
+/*
+ * Precise system-device cross timestamp with extra info.
+ */
+struct ptp_sys_offset_stat {
+	struct ptp_clock_time device;
+	struct ptp_clock_time sys_realtime;
+	struct ptp_clock_time sys_monoraw;
+	struct ptp_clock_accuracy accuracy;
+	unsigned int flags;
+	/*
+	 * One of the ptp_utc_leap enumeration values.
+	 *
+	 * Valid if flag PTP_CLOCK_LEAP_VALID is set.
+	 */
+	unsigned int leap;
+	/*
+	 * Offset of TAI from UTC at the time of clock reading, in the form
+	 * TAI - UTC.
+	 *
+	 * Valid if flag PTP_CLOCK_TAI_OFFSET_VALID is set.
+	 */
+	int tai_offset_sec;
+	/*
+	 * Offset of smeared clock from UTC at the time of clock reading, in
+	 * the form UTC - smeared clock.
+	 *
+	 * Valid if flag PTP_CLOCK_SMEAR_OFFSET_VALID is set.
+	 */
+	int smear_offset_nsec;
+	/* Reserved for future use. */
+	unsigned int rsv[22];
+};
+
 enum ptp_pin_function {
 	PTP_PF_NONE,
 	PTP_PF_EXTTS,
@@ -245,6 +372,7 @@ struct ptp_pin_desc {
 	_IOWR(PTP_CLK_MAGIC, 18, struct ptp_sys_offset_extended)
 #define PTP_MASK_CLEAR_ALL  _IO(PTP_CLK_MAGIC, 19)
 #define PTP_MASK_EN_SINGLE  _IOW(PTP_CLK_MAGIC, 20, unsigned int)
+#define PTP_SYS_OFFSET_STAT _IOWR(PTP_CLK_MAGIC, 21, struct ptp_sys_offset_stat)
 
 struct ptp_extts_event {
 	struct ptp_clock_time t; /* Time event occurred. */
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile
index 8f57f88ecadd..3fa960c72337 100644
--- a/tools/testing/selftests/ptp/Makefile
+++ b/tools/testing/selftests/ptp/Makefile
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 CFLAGS += $(KHDR_INCLUDES)
 TEST_GEN_PROGS := testptp
-LDLIBS += -lrt
+LDLIBS += -lrt -lm
 TEST_PROGS = phc.sh
 
 include ../lib.mk
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
index 58064151f2c8..7d09f4b9272e 100644
--- a/tools/testing/selftests/ptp/testptp.c
+++ b/tools/testing/selftests/ptp/testptp.c
@@ -37,6 +37,8 @@
 
 #define NSEC_PER_SEC 1000000000LL
 
+#define STAT_FRACTIONAL_BITS 44
+
 /* clock_adjtime is not available in GLIBC < 2.14 */
 #if !__GLIBC_PREREQ(2, 14)
 #include <sys/syscall.h>
@@ -147,6 +149,7 @@ static void usage(char *progname)
 		" -x val     get an extended ptp clock time with the desired number of samples (up to %d)\n"
 		" -X         get a ptp clock cross timestamp\n"
 		" -y val     pre/post tstamp timebase to use {realtime|monotonic|monotonic-raw}\n"
+		" -Y         get a ptp clock cross timestamp with extra status information\n"
 		" -z         test combinations of rising/falling external time stamp flags\n",
 		progname, PTP_MAX_SAMPLES);
 }
@@ -164,6 +167,8 @@ int main(int argc, char *argv[])
 	struct ptp_sys_offset *sysoff;
 	struct ptp_sys_offset_extended *soe;
 	struct ptp_sys_offset_precise *xts;
+	struct ptp_sys_offset_stat *sts;
+	struct ptp_clock_accuracy *accuracy;
 
 	char *progname;
 	unsigned int i;
@@ -184,6 +189,7 @@ int main(int argc, char *argv[])
 	int pct_offset = 0;
 	int getextended = 0;
 	int getcross = 0;
+	int getstat = 0;
 	int n_samples = 0;
 	int pin_index = -1, pin_func;
 	int pps = -1;
@@ -191,6 +197,7 @@ int main(int argc, char *argv[])
 	int settime = 0;
 	int channel = -1;
 	clockid_t ext_clockid = CLOCK_REALTIME;
+	double freq_ppm_mult;
 
 	int64_t t1, t2, tp;
 	int64_t interval, offset;
@@ -200,7 +207,7 @@ int main(int argc, char *argv[])
 
 	progname = strrchr(argv[0], '/');
 	progname = progname ? 1+progname : argv[0];
-	while (EOF != (c = getopt(argc, argv, "cd:e:f:F:ghH:i:k:lL:n:o:p:P:sSt:T:w:x:Xy:z"))) {
+	while (EOF != (c = getopt(argc, argv, "cd:e:f:F:ghH:i:k:lL:n:o:p:P:sSt:T:w:x:Xy:Yz"))) {
 		switch (c) {
 		case 'c':
 			capabilities = 1;
@@ -294,7 +301,9 @@ int main(int argc, char *argv[])
 				return -1;
 			}
 			break;
-
+		case 'Y':
+			getstat = 1;
+			break;
 		case 'z':
 			flagtest = 1;
 			break;
@@ -333,7 +342,8 @@ int main(int argc, char *argv[])
 			       "  %d programmable pins\n"
 			       "  %d cross timestamping\n"
 			       "  %d adjust_phase\n"
-			       "  %d maximum phase adjustment (ns)\n",
+			       "  %d maximum phase adjustment (ns)\n"
+			       "  %d cross timestamping with extra status\n",
 			       caps.max_adj,
 			       caps.n_alarm,
 			       caps.n_ext_ts,
@@ -342,7 +352,8 @@ int main(int argc, char *argv[])
 			       caps.n_pins,
 			       caps.cross_timestamping,
 			       caps.adjust_phase,
-			       caps.max_phase_adj);
+			       caps.max_phase_adj,
+			       caps.stat_timestamping);
 		}
 	}
 
@@ -661,6 +672,113 @@ int main(int argc, char *argv[])
 		free(xts);
 	}
 
+	if (getstat) {
+		sts = calloc(1, sizeof(*sts));
+		if (!sts) {
+			perror("calloc");
+			return -1;
+		}
+
+		if (ioctl(fd, PTP_SYS_OFFSET_STAT, sts)) {
+			perror("PTP_SYS_OFFSET_STAT");
+		} else {
+			puts("system and phc crosstimestamping with extra status request okay");
+
+			printf("device time: %lld.%09u\n",
+			       sts->device.sec, sts->device.nsec);
+			printf("system time: %lld.%09u\n",
+			       sts->sys_realtime.sec, sts->sys_realtime.nsec);
+			printf("monoraw time: %lld.%09u\n",
+			       sts->sys_monoraw.sec, sts->sys_monoraw.nsec);
+
+			accuracy = &sts->accuracy;
+			freq_ppm_mult = pow(2, -STAT_FRACTIONAL_BITS);
+			printf("est. frequency error: %e ppm\n",
+			       accuracy->freq_esterror * freq_ppm_mult);
+			printf("max. frequency error: %e ppm\n",
+			       accuracy->freq_maxerror * freq_ppm_mult);
+			printf("est. time error: %llu.%09llu\n",
+			       accuracy->time_esterror / NSEC_PER_SEC,
+			       accuracy->time_esterror % NSEC_PER_SEC);
+			printf("max. time error: %llu.%09llu\n",
+			       accuracy->time_maxerror / NSEC_PER_SEC,
+			       accuracy->time_maxerror % NSEC_PER_SEC);
+			printf("accuracy flags: 0x%x\n", accuracy->flags);
+
+			switch (accuracy->clock_status) {
+			case PTP_CLOCK_STATUS_UNKNOWN:
+				printf("status: unknown\n");
+				break;
+			case PTP_CLOCK_STATUS_INITIALIZING:
+				printf("status: initializing\n");
+				break;
+			case PTP_CLOCK_STATUS_SYNCHRONIZED:
+				printf("status: synchronized\n");
+				break;
+			case PTP_CLOCK_STATUS_FREERUNNING:
+				printf("status: free-running\n");
+				break;
+			case PTP_CLOCK_STATUS_UNRELIABLE:
+				printf("status: unreliable\n");
+				break;
+			default:
+				printf("status: ? (0x%x)\n",
+				       accuracy->clock_status);
+				break;
+			}
+
+			printf("flags: 0x%x\n", sts->flags);
+			switch (sts->leap) {
+			case PTP_LEAP_NONE:
+				printf("leap second: none\n");
+				break;
+			case PTP_LEAP_PRE_POS:
+				printf("leap second: pre-announcing positive leap second\n");
+				break;
+			case PTP_LEAP_PRE_NEG:
+				printf("leap second: pre-announcing negative leap second\n");
+				break;
+			case PTP_LEAP_POS:
+				printf("leap second: positive leap second in progress\n");
+				break;
+			case PTP_LEAP_POST_POS:
+				printf("leap second: past positive leap second\n");
+				break;
+			case PTP_LEAP_POST_NEG:
+				printf("leap second: past negative leap second\n");
+				break;
+			case PTP_LEAP_SMEAR_PRE_POS:
+				printf("leap second: pre-announcing smearing for positive leap second\n");
+				break;
+			case PTP_LEAP_SMEAR_PRE_NEG:
+				printf("leap second: pre-announcing smearing for negative leap second\n");
+				break;
+			case PTP_LEAP_SMEAR_POS:
+				printf("leap second: smearing for positive leap second in progress\n");
+				break;
+			case PTP_LEAP_SMEAR_NEG:
+				printf("leap second: smearing for negative leap second in progress\n");
+				break;
+			case PTP_LEAP_SMEAR_POST_POS:
+				printf("leap second: past smearing for positive leap second\n");
+				break;
+			case PTP_LEAP_SMEAR_POST_NEG:
+				printf("leap second: past smearing for negative leap second\n");
+				break;
+			default:
+				printf("leap second: ? (0x%x)\n", sts->leap);
+				break;
+			}
+			printf("tai offset: %d\n", sts->tai_offset_sec);
+			printf("smear offset: %s%d.%09d\n",
+			       sts->smear_offset_nsec < 0 ? "-" : "",
+			       abs(sts->smear_offset_nsec) / (int)NSEC_PER_SEC,
+			       abs(sts->smear_offset_nsec) % (int)NSEC_PER_SEC);
+		}
+
+		free(sts);
+	}
+
 	if (channel >= 0) {
 		if (ioctl(fd, PTP_MASK_CLEAR_ALL)) {
 			perror("PTP_MASK_CLEAR_ALL");
