diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index d9d566f70ed1..deeecfc0a3ee 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -82,6 +82,14 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
 		goto pps_register_source_exit;
 	}
 
+	if ((info->mode & PPS_WIDTHBOTH) &&
+	    ((info->mode & PPS_CAPTUREBOTH) != PPS_CAPTUREBOTH)) {
+		pr_err("%s: width can't be calculated without both captures (mode = 0x%x)\n",
+		       info->name, info->mode);
+		err = -EINVAL;
+		goto pps_register_source_exit;
+	}
+
 	/* Allocate memory for the new PPS source struct */
 	pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);
 	if (pps == NULL) {
@@ -143,6 +151,39 @@ void pps_unregister_source(struct pps_device *pps)
 }
 EXPORT_SYMBOL(pps_unregister_source);
 
+static u64 pps_ktime_sub(struct pps_ktime *ts1, struct pps_ktime *ts2)
+{
+	if (ts1->sec == ts2->sec)
+		return (ts1->nsec > ts2->nsec) ? (ts1->nsec - ts2->nsec) : (ts2->nsec - ts1->nsec);
+
+	if (ts1->sec > ts2->sec)
+		return (ts1->sec - ts2->sec) * NSEC_PER_SEC + ts1->nsec - ts2->nsec;
+
+	return (ts2->sec - ts1->sec) * NSEC_PER_SEC + ts2->nsec - ts1->nsec;
+}
+
+static void pps_calc_clear_width(struct pps_device *pps)
+{
+	if (pps->clear_sequence == 0)
+		return;
+
+	pps->clear_width.sequence++;
+	pps->clear_width.nsec = pps_ktime_sub(&pps->assert_tu, &pps->clear_tu);
+	dev_dbg(pps->dev, "PPS clear width = %llu#%u\n",
+		pps->clear_width.nsec, pps->clear_width.sequence);
+}
+
+static void pps_calc_assert_width(struct pps_device *pps)
+{
+	if (pps->assert_sequence == 0)
+		return;
+
+	pps->assert_width.sequence++;
+	pps->assert_width.nsec = pps_ktime_sub(&pps->clear_tu, &pps->assert_tu);
+	dev_dbg(pps->dev, "PPS assert width = %llu#%u\n",
+		pps->assert_width.nsec, pps->assert_width.sequence);
+}
+
 /* pps_event - register a PPS event into the system
  * @pps: the PPS device
  * @ts: the event timestamp
@@ -191,6 +232,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
 		dev_dbg(pps->dev, "capture assert seq #%u\n",
 			pps->assert_sequence);
 
+		/* Calculate clear pulse-width */
+		if (pps->params.mode & PPS_WIDTHCLEAR)
+			pps_calc_clear_width(pps);
+
 		captured = ~0;
 	}
 	if (event & pps->params.mode & PPS_CAPTURECLEAR) {
@@ -205,6 +250,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
 		dev_dbg(pps->dev, "capture clear seq #%u\n",
 			pps->clear_sequence);
 
+		/* Calculate assert pulse-width */
+		if (pps->params.mode & PPS_WIDTHASSERT)
+			pps_calc_assert_width(pps);
+
 		captured = ~0;
 	}
 
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 5d19baae6a38..8299a272af11 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -195,6 +195,11 @@ static long pps_cdev_ioctl(struct file *file,
 		fdata.info.clear_tu = pps->clear_tu;
 		fdata.info.current_mode = pps->current_mode;
 
+		memcpy(&fdata.info.assert_width, &pps->assert_width,
+		       sizeof(struct pps_kwidth));
+		memcpy(&fdata.info.clear_width, &pps->clear_width,
+		       sizeof(struct pps_kwidth));
+
 		spin_unlock_irq(&pps->lock);
 
 		err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata));
@@ -283,6 +288,10 @@ static long pps_cdev_compat_ioctl(struct file *file,
 				sizeof(struct pps_ktime_compat));
 		memcpy(&compat.info.clear_tu, &pps->clear_tu,
 				sizeof(struct pps_ktime_compat));
+		memcpy(&compat.info.assert_width, &pps->assert_width,
+		       sizeof(struct pps_kwidth_compat));
+		memcpy(&compat.info.clear_width, &pps->clear_width,
+		       sizeof(struct pps_kwidth_compat));
 
 		spin_unlock_irq(&pps->lock);
 
diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c
index 134bc33f6ad0..3e34de77dba6 100644
--- a/drivers/pps/sysfs.c
+++ b/drivers/pps/sysfs.c
@@ -79,6 +79,34 @@ static ssize_t path_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(path);
 
+static ssize_t pulse_width_assert_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct pps_device *pps = dev_get_drvdata(dev);
+
+	if (!(pps->info.mode & PPS_WIDTHASSERT))
+		return 0;
+
+	return sprintf(buf, "%llu#%u\n",
+		       pps->assert_width.nsec, pps->assert_width.sequence);
+}
+static DEVICE_ATTR_RO(pulse_width_assert);
+
+static ssize_t pulse_width_clear_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct pps_device *pps = dev_get_drvdata(dev);
+
+	if (!(pps->info.mode & PPS_WIDTHCLEAR))
+		return 0;
+
+	return sprintf(buf, "%llu#%u\n",
+		       pps->clear_width.nsec, pps->clear_width.sequence);
+}
+static DEVICE_ATTR_RO(pulse_width_clear);
+
 static struct attribute *pps_attrs[] = {
 	&dev_attr_assert.attr,
 	&dev_attr_clear.attr,
@@ -86,6 +114,8 @@ static struct attribute *pps_attrs[] = {
 	&dev_attr_echo.attr,
 	&dev_attr_name.attr,
 	&dev_attr_path.attr,
+	&dev_attr_pulse_width_assert.attr,
+	&dev_attr_pulse_width_clear.attr,
 	NULL,
 };
 
diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h
index 78c8ac4951b5..15f2338095c6 100644
--- a/include/linux/pps_kernel.h
+++ b/include/linux/pps_kernel.h
@@ -51,6 +51,9 @@ struct pps_device {
 	struct pps_ktime clear_tu;
 	int current_mode;			/* PPS mode at event time */
 
+	struct pps_kwidth assert_width;		/* PPS assert pulse-width time and event seq # */
+	struct pps_kwidth clear_width;		/* PPS clear pulse-width time and event seq # */
+
 	unsigned int last_ev;			/* last PPS event id */
 	wait_queue_head_t queue;		/* PPS event queue */
 
diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h
index 009ebcd8ced5..dd93dac0afc1 100644
--- a/include/uapi/linux/pps.h
+++ b/include/uapi/linux/pps.h
@@ -64,12 +64,24 @@ struct pps_ktime_compat {
 } __attribute__((packed, aligned(4)));
 #define PPS_TIME_INVALID	(1<<0)	/* used to specify timeout==NULL */
 
+struct pps_kwidth {
+	__u64 nsec;
+	__u32 sequence;
+};
+
+struct pps_kwidth_compat {
+	__u64 nsec;
+	__u32 sequence;
+} __attribute__((packed, aligned(4)));
+
 struct pps_kinfo {
 	__u32 assert_sequence;		/* seq. num. of assert event */
 	__u32 clear_sequence; 		/* seq. num. of clear event */
 	struct pps_ktime assert_tu;	/* time of assert event */
 	struct pps_ktime clear_tu;	/* time of clear event */
 	int current_mode;		/* current mode bits */
+	struct pps_kwidth assert_width;	/* assert pulse-width time and seq. num. */
+	struct pps_kwidth clear_width;	/* clear pulse-width time and seq. num. */
 };
 
 struct pps_kinfo_compat {
@@ -78,6 +90,8 @@ struct pps_kinfo_compat {
 	struct pps_ktime_compat assert_tu;	/* time of assert event */
 	struct pps_ktime_compat clear_tu;	/* time of clear event */
 	int current_mode;			/* current mode bits */
+	struct pps_kwidth_compat assert_width;	/* assert pulse-width time and seq. num. */
+	struct pps_kwidth_compat clear_width;	/* clear pulse-width time and seq. num. */
 };
 
 struct pps_kparams {
@@ -96,6 +110,11 @@ struct pps_kparams {
 #define PPS_CAPTURECLEAR	0x02	/* capture clear events */
 #define PPS_CAPTUREBOTH		0x03	/* capture assert and clear events */
 
+/* Pulse-width calculation */
+#define PPS_WIDTHASSERT		0x04	/* calculate assert width */
+#define PPS_WIDTHCLEAR		0x08	/* calculate clear width */
+#define PPS_WIDTHBOTH		0x0c	/* calculate assert and clear width */
+
 #define PPS_OFFSETASSERT	0x10	/* apply compensation for assert event */
 #define PPS_OFFSETCLEAR		0x20	/* apply compensation for clear event */
 
