[PATCH 1/1] Calculate the monotonic clock from the timespec clock to generate PPS elapsed real-time event value and stores the result into /sys/class/pps/pps0/assert_elapsed.

From: Alexander Komrakov
Date: Wed Nov 17 2021 - 22:22:33 EST


From: alexkom <alexander.komrakov@xxxxxxxxxxxx>

Because we have requirements to make sure the delta between standard time, say the GPS Time, and elapsedRealtime < 1 millisecond, regular linux clock timestamp is not enough for our use case.
The pin PPS will generate elapsedRealtime event at 1 sec boundary which is an exact value of the monotonic clock from the kernel PPS driver (/sys/class/pps/pps0/assert_elapsed).

Whenever AP receives this pulse, kernel's pps driver timestamp this elapsedRealtime event and let this time available via sysfs node (/sys/class/pps/pps0/assert_elapsed).

Signed-off-by: alexkom <alexander.komrakov@xxxxxxxxxxxx>
---
Documentation/ABI/testing/sysfs-pps | 24 ++++++++++++++++++
drivers/pps/kapi.c | 38 ++++++++++++++++++++++++++---
drivers/pps/sysfs.c | 33 +++++++++++++++++++++++++
include/linux/pps_kernel.h | 2 ++
4 files changed, 94 insertions(+), 3 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-pps b/Documentation/ABI/testing/sysfs-pps
index 25028c7bc37d..054617abeebd 100644
--- a/Documentation/ABI/testing/sysfs-pps
+++ b/Documentation/ABI/testing/sysfs-pps
@@ -1,3 +1,27 @@
+What: /sys/class/pps/pps0/assert_elapsed
+Date: October 2021
+Contact: Alexander Komrakov <alexander.komrakov@xxxxxxxxxxxx>
+Description:
+ The /sys/class/pps/ppsX/assert_elapsed file reports the elapsed real-time assert events
+ and the elapsed real-time assert sequence number of the X-th source in the form:
+
+ <secs>.<nsec>#<sequence>
+
+ If the source has no elapsed real-time assert events the content of this file
+ is empty.
+
+What: /sys/class/pps/ppsX/clear_elapsed
+Date: October 2021
+Contact: Alexander Komrakov <alexander.komrakov@xxxxxxxxxxxx>
+Description:
+ The /sys/class/pps/ppsX/clear_elapsed file reports the elapsed real-time clear events
+ and the elapsed real-time clear sequence number of the X-th source in the form:
+
+ <secs>.<nsec>#<sequence>
+
+ If the source has no elapsed real-time clear events the content of this file
+ is empty.
+
What: /sys/class/pps/
Date: February 2008
Contact: Rodolfo Giometti <giometti@xxxxxxxx>
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index d9d566f70ed1..149c29e498fc 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -23,6 +23,26 @@
/*
* Local functions
*/
+ #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
+
+/**
+ * clock_gettime - get the monotonic clock in pps_ktime format
+ * @kt: pointer to the pps_ktime to be set to raw monotonic time
+ *
+ * The function calculates the monotonic clock from the timespec clock
+ * and stores the result in pps_ktime format in the variable pointed to by @kt.
+ *
+ * The function returns the monotonic clock normalized format in nanosec.
+ */
+static __u64 clock_gettime(struct pps_ktime *kt)
+{
+ struct timespec64 ts = { .tv_sec = 0, .tv_nsec = 0 };
+
+ ktime_get_ts64(&ts);
+ kt->sec = ts.tv_sec;
+ kt->nsec = ts.tv_nsec;
+ return (__u64) ts.tv_sec * NANOSEC_PER_SEC + ts.tv_nsec;
+}

static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
{
@@ -162,11 +182,15 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
unsigned long flags;
int captured = 0;
struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 };
+ struct pps_ktime ts_real_elapsed = { .sec = 0, .nsec = 0, .flags = 0 };

/* check event type */
BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);
+ clock_gettime(&ts_real_elapsed);
+ dev_dbg(pps->dev, "PPS event (monotonic) at %lld.%09d\n",
+ (s64)ts_real_elapsed.sec, ts_real_elapsed.nsec);

- dev_dbg(pps->dev, "PPS event at %lld.%09ld\n",
+ dev_dbg(pps->dev, "PPS event (timestamp) at %lld.%09ld\n",
(s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec);

timespec_to_pps_ktime(&ts_real, ts->ts_real);
@@ -181,11 +205,15 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
pps->current_mode = pps->params.mode;
if (event & pps->params.mode & PPS_CAPTUREASSERT) {
/* We have to add an offset? */
- if (pps->params.mode & PPS_OFFSETASSERT)
+ if (pps->params.mode & PPS_OFFSETASSERT) {
+ pps_add_offset(&ts_real_elapsed,
+ &pps->params.assert_off_tu);
pps_add_offset(&ts_real,
&pps->params.assert_off_tu);
+ }

/* Save the time stamp */
+ pps->assert_elapsed_tu = ts_real_elapsed;
pps->assert_tu = ts_real;
pps->assert_sequence++;
dev_dbg(pps->dev, "capture assert seq #%u\n",
@@ -195,11 +223,15 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
}
if (event & pps->params.mode & PPS_CAPTURECLEAR) {
/* We have to add an offset? */
- if (pps->params.mode & PPS_OFFSETCLEAR)
+ if (pps->params.mode & PPS_OFFSETCLEAR) {
+ pps_add_offset(&ts_real_elapsed,
+ &pps->params.clear_off_tu);
pps_add_offset(&ts_real,
&pps->params.clear_off_tu);
+ }

/* Save the time stamp */
+ pps->clear_elapsed_tu = ts_real_elapsed;
pps->clear_tu = ts_real;
pps->clear_sequence++;
dev_dbg(pps->dev, "capture clear seq #%u\n",
diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c
index 134bc33f6ad0..97721cb01695 100644
--- a/drivers/pps/sysfs.c
+++ b/drivers/pps/sysfs.c
@@ -29,6 +29,21 @@ static ssize_t assert_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(assert);

+static ssize_t assert_elapsed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pps_device *pps = dev_get_drvdata(dev);
+
+ if (!(pps->info.mode & PPS_CAPTUREASSERT))
+ return 0;
+
+ return sprintf(buf, "%lld.%09d#%d\n",
+ (long long) pps->assert_elapsed_tu.sec,
+ pps->assert_elapsed_tu.nsec,
+ pps->assert_sequence);
+}
+static DEVICE_ATTR_RO(assert_elapsed);
+
static ssize_t clear_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -43,6 +58,22 @@ static ssize_t clear_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(clear);

+static ssize_t clear_elapsed_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pps_device *pps = dev_get_drvdata(dev);
+
+ if (!(pps->info.mode & PPS_CAPTURECLEAR))
+ return 0;
+
+ return sprintf(buf, "%lld.%09d#%d\n",
+ (long long) pps->clear_elapsed_tu.sec,
+ pps->clear_elapsed_tu.nsec,
+ pps->clear_sequence);
+}
+static DEVICE_ATTR_RO(clear_elapsed);
+
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -81,7 +112,9 @@ static DEVICE_ATTR_RO(path);

static struct attribute *pps_attrs[] = {
&dev_attr_assert.attr,
+ &dev_attr_assert_elapsed.attr,
&dev_attr_clear.attr,
+ &dev_attr_clear_elapsed.attr,
&dev_attr_mode.attr,
&dev_attr_echo.attr,
&dev_attr_name.attr,
diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h
index 78c8ac4951b5..1fecaaf4c8b9 100644
--- a/include/linux/pps_kernel.h
+++ b/include/linux/pps_kernel.h
@@ -47,6 +47,8 @@ struct pps_device {

__u32 assert_sequence; /* PPS assert event seq # */
__u32 clear_sequence; /* PPS clear event seq # */
+ struct pps_ktime assert_elapsed_tu; /* PPS elapsed rt assert seq # */
+ struct pps_ktime clear_elapsed_tu; /* PPS elapsed rt clear event seq */
struct pps_ktime assert_tu;
struct pps_ktime clear_tu;
int current_mode; /* PPS mode at event time */
--
2.25.1


--
This electronic communication and the information and any files transmitted
with it, or attached to it, are confidential and are intended solely for
the use of the individual or entity to whom it is addressed and may contain
information that is confidential, legally privileged, protected by privacy
laws, or otherwise restricted from disclosure to anyone else. If you are
not the intended recipient or the person responsible for delivering the
e-mail to the intended recipient, you are hereby notified that any use,
copying, distributing, dissemination, forwarding, printing, or copying of
this e-mail is strictly prohibited. If you received this e-mail in error,
please return the e-mail to the sender, delete it from your computer, and
destroy any printed copy of it.

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature