ptp: Add .getmaxphase callback to ptp_clock_info
authorRahul Rameshbabu <rrameshbabu@nvidia.com>
Mon, 12 Jun 2023 21:14:56 +0000 (14:14 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Jun 2023 08:02:33 +0000 (09:02 +0100)
Enables advertisement of the maximum offset supported by the phase control
functionality of PHCs. The callback is used to return an error if an offset
not supported by the PHC is used in ADJ_OFFSET. The ioctls
PTP_CLOCK_GETCAPS and PTP_CLOCK_GETCAPS2 now advertise the maximum offset a
PHC's phase control functionality is capable of supporting. Introduce new
sysfs node, max_phase_adjustment.

Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Richard Cochran <richardcochran@gmail.com>
Cc: Maciek Machnikowski <maciek@machnikowski.net>
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/ptp/ptp_chardev.c
drivers/ptp/ptp_clock.c
drivers/ptp/ptp_sysfs.c
include/linux/ptp_clock_kernel.h
include/uapi/linux/ptp_clock.h
tools/testing/selftests/ptp/testptp.c

index af3bc65c4595dd50782ff2e02a2c17d0f6d0b670..362bf756e6b78b5793d0f8da7132def435195e5b 100644 (file)
@@ -136,7 +136,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                caps.pps = ptp->info->pps;
                caps.n_pins = ptp->info->n_pins;
                caps.cross_timestamping = ptp->info->getcrosststamp != NULL;
-               caps.adjust_phase = ptp->info->adjphase != NULL;
+               caps.adjust_phase = ptp->info->adjphase != NULL &&
+                                   ptp->info->getmaxphase != NULL;
+               if (caps.adjust_phase)
+                       caps.max_phase_adj = ptp->info->getmaxphase(ptp->info);
                if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
                        err = -EFAULT;
                break;
index 790f9250b3815b27dbf1011d481a0bb31666fc54..80f74e38c2da4b5fac16a560dea2049455829fbe 100644 (file)
@@ -135,11 +135,15 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
                ptp->dialed_frequency = tx->freq;
        } else if (tx->modes & ADJ_OFFSET) {
                if (ops->adjphase) {
+                       s32 max_phase_adj = ops->getmaxphase(ops);
                        s32 offset = tx->offset;
 
                        if (!(tx->modes & ADJ_NANO))
                                offset *= NSEC_PER_USEC;
 
+                       if (offset > max_phase_adj || offset < -max_phase_adj)
+                               return -ERANGE;
+
                        err = ops->adjphase(ops, offset);
                }
        } else if (tx->modes == 0) {
index f30b0a439470549c77288d3ce20a445072d27b8f..77219cdcd6831557a89b7d72e5d53fbefb733277 100644 (file)
@@ -18,6 +18,17 @@ static ssize_t clock_name_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(clock_name);
 
+static ssize_t max_phase_adjustment_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->info->getmaxphase(ptp->info));
+}
+static DEVICE_ATTR_RO(max_phase_adjustment);
+
 #define PTP_SHOW_INT(name, var)                                                \
 static ssize_t var##_show(struct device *dev,                          \
                           struct device_attribute *attr, char *page)   \
@@ -309,6 +320,7 @@ static struct attribute *ptp_attrs[] = {
        &dev_attr_clock_name.attr,
 
        &dev_attr_max_adjustment.attr,
+       &dev_attr_max_phase_adjustment.attr,
        &dev_attr_n_alarms.attr,
        &dev_attr_n_external_timestamps.attr,
        &dev_attr_n_periodic_outputs.attr,
index f8e8443a8b358b7ec9853ad0000d680815db0cec..1ef4e0f9bd2a5d61352987b4af7240c663a25cf9 100644 (file)
@@ -82,6 +82,10 @@ struct ptp_system_timestamp {
  *             parameter delta: PHC servo phase adjustment target
  *                              in nanoseconds.
  *
+ * @getmaxphase:  Advertises maximum offset that can be provided
+ *                to the hardware clock's phase control functionality
+ *                through adjphase.
+ *
  * @adjtime:  Shifts the time of the hardware clock.
  *            parameter delta: Desired change in nanoseconds.
  *
@@ -171,6 +175,7 @@ struct ptp_clock_info {
        struct ptp_pin_desc *pin_config;
        int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);
        int (*adjphase)(struct ptp_clock_info *ptp, s32 phase);
+       s32 (*getmaxphase)(struct ptp_clock_info *ptp);
        int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
        int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
        int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
index 1d108d597f66d797871de68851eb8eab793ad522..05cc35fc94acf8124268e4b222fcaa88a7809759 100644 (file)
@@ -95,7 +95,8 @@ struct ptp_clock_caps {
        int cross_timestamping;
        /* Whether the clock supports adjust phase */
        int adjust_phase;
-       int rsv[12];   /* Reserved for future use. */
+       int max_phase_adj; /* Maximum phase adjustment in nanoseconds. */
+       int rsv[11];       /* Reserved for future use. */
 };
 
 struct ptp_extts_request {
index cc535f76db9919b0c8d9e39fd775a21fae7b2ed6..e9438a1862adfe57c85817b20554a8cf1ceae4d4 100644 (file)
@@ -292,7 +292,8 @@ int main(int argc, char *argv[])
                               "  %d pulse per second\n"
                               "  %d programmable pins\n"
                               "  %d cross timestamping\n"
-                              "  %d adjust_phase\n",
+                              "  %d adjust_phase\n"
+                              "  %d maximum phase adjustment (ns)\n",
                               caps.max_adj,
                               caps.n_alarm,
                               caps.n_ext_ts,
@@ -300,7 +301,8 @@ int main(int argc, char *argv[])
                               caps.pps,
                               caps.n_pins,
                               caps.cross_timestamping,
-                              caps.adjust_phase);
+                              caps.adjust_phase,
+                              caps.max_phase_adj);
                }
        }