diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index af3bc65c4595d..362bf756e6b78 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -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; diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 790f9250b3815..80f74e38c2da4 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -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) { diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c index f30b0a4394705..77219cdcd6831 100644 --- a/drivers/ptp/ptp_sysfs.c +++ b/drivers/ptp/ptp_sysfs.c @@ -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, diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index f8e8443a8b358..1ef4e0f9bd2a5 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -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, diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h index 1d108d597f66d..05cc35fc94acf 100644 --- a/include/uapi/linux/ptp_clock.h +++ b/include/uapi/linux/ptp_clock.h @@ -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 { diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c index cc535f76db991..e9438a1862adf 100644 --- a/tools/testing/selftests/ptp/testptp.c +++ b/tools/testing/selftests/ptp/testptp.c @@ -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); } }