Commit 712557f2 authored by David S. Miller's avatar David S. Miller

Merge branch 'ptp-adjphase-cleanups'

Rahul Rameshbabu says:

====================
ptp .adjphase cleanups

The goal of this patch series is to improve documentation of .adjphase, add
a new callback .getmaxphase to enable advertising the max phase offset a
device PHC can support, and support invoking .adjphase from the testptp
kselftest.

Changes:
  v2->v1:
    * Removes arbitrary rule that the PHC servo must restore the frequency
      to the value used in the last .adjfine call if any other PHC
      operation is used after a .adjphase operation.
    * Removes a macro introduced in v1 for adding PTP sysfs device
      attribute nodes using a callback for populating the data.

Link: https://lore.kernel.org/netdev/20230120160609.19160723@kernel.org/
Link: https://lore.kernel.org/netdev/20230510205306.136766-1-rrameshbabu@nvidia.com/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 49310624 d8ee5ca8
...@@ -73,6 +73,22 @@ Writing clock drivers ...@@ -73,6 +73,22 @@ Writing clock drivers
class driver, since the lock may also be needed by the clock class driver, since the lock may also be needed by the clock
driver's interrupt service routine. driver's interrupt service routine.
PTP hardware clock requirements for '.adjphase'
-----------------------------------------------
The 'struct ptp_clock_info' interface has a '.adjphase' function.
This function has a set of requirements from the PHC in order to be
implemented.
* The PHC implements a servo algorithm internally that is used to
correct the offset passed in the '.adjphase' call.
* When other PTP adjustment functions are called, the PHC servo
algorithm is disabled.
**NOTE:** '.adjphase' is not a simple time adjustment functionality
that 'jumps' the PHC clock time based on the provided offset. It
should correct the offset provided using an internal algorithm.
Supported hardware Supported hardware
================== ==================
...@@ -106,3 +122,16 @@ Supported hardware ...@@ -106,3 +122,16 @@ Supported hardware
- LPF settings (bandwidth, phase limiting, automatic holdover, physical layer assist (per ITU-T G.8273.2)) - LPF settings (bandwidth, phase limiting, automatic holdover, physical layer assist (per ITU-T G.8273.2))
- Programmable output PTP clocks, any frequency up to 1GHz (to other PHY/MAC time stampers, refclk to ASSPs/SoCs/FPGAs) - Programmable output PTP clocks, any frequency up to 1GHz (to other PHY/MAC time stampers, refclk to ASSPs/SoCs/FPGAs)
- Lock to GNSS input, automatic switching between GNSS and user-space PHC control (optional) - Lock to GNSS input, automatic switching between GNSS and user-space PHC control (optional)
* NVIDIA Mellanox
- GPIO
- Certain variants of ConnectX-6 Dx and later products support one
GPIO which can time stamp external triggers and one GPIO to produce
periodic signals.
- Certain variants of ConnectX-5 and older products support one GPIO,
configured to either time stamp external triggers or produce
periodic signals.
- PHC instances
- All ConnectX devices have a free-running counter
- ConnectX-6 Dx and later devices have a UTC format counter
...@@ -93,17 +93,23 @@ static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev) ...@@ -93,17 +93,23 @@ static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev)
return MLX5_CAP_MCAM_FEATURE(mdev, ptpcyc2realtime_modify); return MLX5_CAP_MCAM_FEATURE(mdev, ptpcyc2realtime_modify);
} }
static bool mlx5_is_mtutc_time_adj_cap(struct mlx5_core_dev *mdev, s64 delta) static s32 mlx5_ptp_getmaxphase(struct ptp_clock_info *ptp)
{ {
s64 min = MLX5_MTUTC_OPERATION_ADJUST_TIME_MIN; struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
s64 max = MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX; struct mlx5_core_dev *mdev;
if (MLX5_CAP_MCAM_FEATURE(mdev, mtutc_time_adjustment_extended_range)) { mdev = container_of(clock, struct mlx5_core_dev, clock);
min = MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MIN;
max = MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX; return MLX5_CAP_MCAM_FEATURE(mdev, mtutc_time_adjustment_extended_range) ?
} MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX :
MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX;
}
static bool mlx5_is_mtutc_time_adj_cap(struct mlx5_core_dev *mdev, s64 delta)
{
s64 max = mlx5_ptp_getmaxphase(&mdev->clock.ptp_info);
if (delta < min || delta > max) if (delta < -max || delta > max)
return false; return false;
return true; return true;
...@@ -351,14 +357,6 @@ static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -351,14 +357,6 @@ static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
static int mlx5_ptp_adjphase(struct ptp_clock_info *ptp, s32 delta) static int mlx5_ptp_adjphase(struct ptp_clock_info *ptp, s32 delta)
{ {
struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
struct mlx5_core_dev *mdev;
mdev = container_of(clock, struct mlx5_core_dev, clock);
if (!mlx5_is_mtutc_time_adj_cap(mdev, delta))
return -ERANGE;
return mlx5_ptp_adjtime(ptp, delta); return mlx5_ptp_adjtime(ptp, delta);
} }
...@@ -734,6 +732,7 @@ static const struct ptp_clock_info mlx5_ptp_clock_info = { ...@@ -734,6 +732,7 @@ static const struct ptp_clock_info mlx5_ptp_clock_info = {
.pps = 0, .pps = 0,
.adjfine = mlx5_ptp_adjfine, .adjfine = mlx5_ptp_adjfine,
.adjphase = mlx5_ptp_adjphase, .adjphase = mlx5_ptp_adjphase,
.getmaxphase = mlx5_ptp_getmaxphase,
.adjtime = mlx5_ptp_adjtime, .adjtime = mlx5_ptp_adjtime,
.gettimex64 = mlx5_ptp_gettimex, .gettimex64 = mlx5_ptp_gettimex,
.settime64 = mlx5_ptp_settime, .settime64 = mlx5_ptp_settime,
......
...@@ -136,7 +136,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) ...@@ -136,7 +136,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
caps.pps = ptp->info->pps; caps.pps = ptp->info->pps;
caps.n_pins = ptp->info->n_pins; caps.n_pins = ptp->info->n_pins;
caps.cross_timestamping = ptp->info->getcrosststamp != NULL; 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))) if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
err = -EFAULT; err = -EFAULT;
break; break;
......
...@@ -135,11 +135,15 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx) ...@@ -135,11 +135,15 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
ptp->dialed_frequency = tx->freq; ptp->dialed_frequency = tx->freq;
} else if (tx->modes & ADJ_OFFSET) { } else if (tx->modes & ADJ_OFFSET) {
if (ops->adjphase) { if (ops->adjphase) {
s32 max_phase_adj = ops->getmaxphase(ops);
s32 offset = tx->offset; s32 offset = tx->offset;
if (!(tx->modes & ADJ_NANO)) if (!(tx->modes & ADJ_NANO))
offset *= NSEC_PER_USEC; offset *= NSEC_PER_USEC;
if (offset > max_phase_adj || offset < -max_phase_adj)
return -ERANGE;
err = ops->adjphase(ops, offset); err = ops->adjphase(ops, offset);
} }
} else if (tx->modes == 0) { } else if (tx->modes == 0) {
......
...@@ -1692,14 +1692,23 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel) ...@@ -1692,14 +1692,23 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel)
/* PTP Hardware Clock interface */ /* PTP Hardware Clock interface */
/* /*
* Maximum absolute value for write phase offset in picoseconds * Maximum absolute value for write phase offset in nanoseconds
*
* @channel: channel
* @delta_ns: delta in nanoseconds
* *
* Destination signed register is 32-bit register in resolution of 50ps * Destination signed register is 32-bit register in resolution of 50ps
* *
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 ps
* Represent 107374182350 ps as 107374182 ns
*/
static s32 idtcm_getmaxphase(struct ptp_clock_info *ptp __always_unused)
{
return MAX_ABS_WRITE_PHASE_NANOSECONDS;
}
/*
* Internal function for implementing support for write phase offset
*
* @channel: channel
* @delta_ns: delta in nanoseconds
*/ */
static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
{ {
...@@ -1708,7 +1717,6 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) ...@@ -1708,7 +1717,6 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
u8 i; u8 i;
u8 buf[4] = {0}; u8 buf[4] = {0};
s32 phase_50ps; s32 phase_50ps;
s64 offset_ps;
if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) { if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) {
err = channel->configure_write_phase(channel); err = channel->configure_write_phase(channel);
...@@ -1716,19 +1724,7 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) ...@@ -1716,19 +1724,7 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
return err; return err;
} }
offset_ps = (s64)delta_ns * 1000; phase_50ps = div_s64((s64)delta_ns * 1000, 50);
/*
* Check for 32-bit signed max * 50:
*
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
*/
if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
phase_50ps = div_s64(offset_ps, 50);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
buf[i] = phase_50ps & 0xff; buf[i] = phase_50ps & 0xff;
...@@ -2048,6 +2044,7 @@ static const struct ptp_clock_info idtcm_caps = { ...@@ -2048,6 +2044,7 @@ static const struct ptp_clock_info idtcm_caps = {
.n_ext_ts = MAX_TOD, .n_ext_ts = MAX_TOD,
.n_pins = MAX_REF_CLK, .n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase, .adjphase = &idtcm_adjphase,
.getmaxphase = &idtcm_getmaxphase,
.adjfine = &idtcm_adjfine, .adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime, .adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime, .gettime64 = &idtcm_gettime,
...@@ -2064,6 +2061,7 @@ static const struct ptp_clock_info idtcm_caps_deprecated = { ...@@ -2064,6 +2061,7 @@ static const struct ptp_clock_info idtcm_caps_deprecated = {
.n_ext_ts = MAX_TOD, .n_ext_ts = MAX_TOD,
.n_pins = MAX_REF_CLK, .n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase, .adjphase = &idtcm_adjphase,
.getmaxphase = &idtcm_getmaxphase,
.adjfine = &idtcm_adjfine, .adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime_deprecated, .adjtime = &idtcm_adjtime_deprecated,
.gettime64 = &idtcm_gettime, .gettime64 = &idtcm_gettime,
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define MAX_PLL (8) #define MAX_PLL (8)
#define MAX_REF_CLK (16) #define MAX_REF_CLK (16)
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL) #define MAX_ABS_WRITE_PHASE_NANOSECONDS (107374182L)
#define TOD_MASK_ADDR (0xFFA5) #define TOD_MASK_ADDR (0xFFA5)
#define DEFAULT_TOD_MASK (0x04) #define DEFAULT_TOD_MASK (0x04)
......
...@@ -978,24 +978,23 @@ static int idt82p33_enable(struct ptp_clock_info *ptp, ...@@ -978,24 +978,23 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
return err; return err;
} }
static s32 idt82p33_getmaxphase(__always_unused struct ptp_clock_info *ptp)
{
return WRITE_PHASE_OFFSET_LIMIT;
}
static int idt82p33_adjwritephase(struct ptp_clock_info *ptp, s32 offset_ns) static int idt82p33_adjwritephase(struct ptp_clock_info *ptp, s32 offset_ns)
{ {
struct idt82p33_channel *channel = struct idt82p33_channel *channel =
container_of(ptp, struct idt82p33_channel, caps); container_of(ptp, struct idt82p33_channel, caps);
struct idt82p33 *idt82p33 = channel->idt82p33; struct idt82p33 *idt82p33 = channel->idt82p33;
s64 offset_regval, offset_fs; s64 offset_regval;
u8 val[4] = {0}; u8 val[4] = {0};
int err; int err;
offset_fs = (s64)(-offset_ns) * 1000000;
if (offset_fs > WRITE_PHASE_OFFSET_LIMIT)
offset_fs = WRITE_PHASE_OFFSET_LIMIT;
else if (offset_fs < -WRITE_PHASE_OFFSET_LIMIT)
offset_fs = -WRITE_PHASE_OFFSET_LIMIT;
/* Convert from phaseoffset_fs to register value */ /* Convert from phaseoffset_fs to register value */
offset_regval = div_s64(offset_fs * 1000, IDT_T0DPLL_PHASE_RESOL); offset_regval = div_s64((s64)(-offset_ns) * 1000000000ll,
IDT_T0DPLL_PHASE_RESOL);
val[0] = offset_regval & 0xFF; val[0] = offset_regval & 0xFF;
val[1] = (offset_regval >> 8) & 0xFF; val[1] = (offset_regval >> 8) & 0xFF;
...@@ -1175,6 +1174,7 @@ static void idt82p33_caps_init(u32 index, struct ptp_clock_info *caps, ...@@ -1175,6 +1174,7 @@ static void idt82p33_caps_init(u32 index, struct ptp_clock_info *caps,
caps->n_ext_ts = MAX_PHC_PLL, caps->n_ext_ts = MAX_PHC_PLL,
caps->n_pins = max_pins, caps->n_pins = max_pins,
caps->adjphase = idt82p33_adjwritephase, caps->adjphase = idt82p33_adjwritephase,
caps->getmaxphase = idt82p33_getmaxphase,
caps->adjfine = idt82p33_adjfine; caps->adjfine = idt82p33_adjfine;
caps->adjtime = idt82p33_adjtime; caps->adjtime = idt82p33_adjtime;
caps->gettime64 = idt82p33_gettime; caps->gettime64 = idt82p33_gettime;
......
...@@ -43,9 +43,9 @@ ...@@ -43,9 +43,9 @@
#define DEFAULT_OUTPUT_MASK_PLL1 DEFAULT_OUTPUT_MASK_PLL0 #define DEFAULT_OUTPUT_MASK_PLL1 DEFAULT_OUTPUT_MASK_PLL0
/** /**
* @brief Maximum absolute value for write phase offset in femtoseconds * @brief Maximum absolute value for write phase offset in nanoseconds
*/ */
#define WRITE_PHASE_OFFSET_LIMIT (20000052084ll) #define WRITE_PHASE_OFFSET_LIMIT (20000l)
/** @brief Phase offset resolution /** @brief Phase offset resolution
* *
......
...@@ -1124,6 +1124,12 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) ...@@ -1124,6 +1124,12 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static s32
ptp_ocp_null_getmaxphase(struct ptp_clock_info *ptp_info)
{
return 0;
}
static int static int
ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns)
{ {
...@@ -1239,6 +1245,7 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { ...@@ -1239,6 +1245,7 @@ static const struct ptp_clock_info ptp_ocp_clock_info = {
.adjtime = ptp_ocp_adjtime, .adjtime = ptp_ocp_adjtime,
.adjfine = ptp_ocp_null_adjfine, .adjfine = ptp_ocp_null_adjfine,
.adjphase = ptp_ocp_null_adjphase, .adjphase = ptp_ocp_null_adjphase,
.getmaxphase = ptp_ocp_null_getmaxphase,
.enable = ptp_ocp_enable, .enable = ptp_ocp_enable,
.verify = ptp_ocp_verify, .verify = ptp_ocp_verify,
.pps = true, .pps = true,
......
...@@ -18,6 +18,17 @@ static ssize_t clock_name_show(struct device *dev, ...@@ -18,6 +18,17 @@ static ssize_t clock_name_show(struct device *dev,
} }
static DEVICE_ATTR_RO(clock_name); 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) \ #define PTP_SHOW_INT(name, var) \
static ssize_t var##_show(struct device *dev, \ static ssize_t var##_show(struct device *dev, \
struct device_attribute *attr, char *page) \ struct device_attribute *attr, char *page) \
...@@ -309,6 +320,7 @@ static struct attribute *ptp_attrs[] = { ...@@ -309,6 +320,7 @@ static struct attribute *ptp_attrs[] = {
&dev_attr_clock_name.attr, &dev_attr_clock_name.attr,
&dev_attr_max_adjustment.attr, &dev_attr_max_adjustment.attr,
&dev_attr_max_phase_adjustment.attr,
&dev_attr_n_alarms.attr, &dev_attr_n_alarms.attr,
&dev_attr_n_external_timestamps.attr, &dev_attr_n_external_timestamps.attr,
&dev_attr_n_periodic_outputs.attr, &dev_attr_n_periodic_outputs.attr,
......
...@@ -77,8 +77,14 @@ struct ptp_system_timestamp { ...@@ -77,8 +77,14 @@ struct ptp_system_timestamp {
* nominal frequency in parts per million, but with a * nominal frequency in parts per million, but with a
* 16 bit binary fractional field. * 16 bit binary fractional field.
* *
* @adjphase: Adjusts the phase offset of the hardware clock. * @adjphase: Indicates that the PHC should use an internal servo
* parameter delta: Desired change in nanoseconds. * algorithm to correct the provided phase offset.
* 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. * @adjtime: Shifts the time of the hardware clock.
* parameter delta: Desired change in nanoseconds. * parameter delta: Desired change in nanoseconds.
...@@ -169,6 +175,7 @@ struct ptp_clock_info { ...@@ -169,6 +175,7 @@ struct ptp_clock_info {
struct ptp_pin_desc *pin_config; struct ptp_pin_desc *pin_config;
int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm); int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);
int (*adjphase)(struct ptp_clock_info *ptp, s32 phase); 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 (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts, int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
......
...@@ -95,7 +95,8 @@ struct ptp_clock_caps { ...@@ -95,7 +95,8 @@ struct ptp_clock_caps {
int cross_timestamping; int cross_timestamping;
/* Whether the clock supports adjust phase */ /* Whether the clock supports adjust phase */
int 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 { struct ptp_extts_request {
......
...@@ -110,7 +110,7 @@ static long ppb_to_scaled_ppm(int ppb) ...@@ -110,7 +110,7 @@ static long ppb_to_scaled_ppm(int ppb)
static int64_t pctns(struct ptp_clock_time *t) static int64_t pctns(struct ptp_clock_time *t)
{ {
return t->sec * 1000000000LL + t->nsec; return t->sec * NSEC_PER_SEC + t->nsec;
} }
static void usage(char *progname) static void usage(char *progname)
...@@ -134,6 +134,7 @@ static void usage(char *progname) ...@@ -134,6 +134,7 @@ static void usage(char *progname)
" 1 - external time stamp\n" " 1 - external time stamp\n"
" 2 - periodic output\n" " 2 - periodic output\n"
" -n val shift the ptp clock time by 'val' nanoseconds\n" " -n val shift the ptp clock time by 'val' nanoseconds\n"
" -o val phase offset (in nanoseconds) to be provided to the PHC servo\n"
" -p val enable output with a period of 'val' nanoseconds\n" " -p val enable output with a period of 'val' nanoseconds\n"
" -H val set output phase to 'val' nanoseconds (requires -p)\n" " -H val set output phase to 'val' nanoseconds (requires -p)\n"
" -w val set output pulse width to 'val' nanoseconds (requires -p)\n" " -w val set output pulse width to 'val' nanoseconds (requires -p)\n"
...@@ -167,6 +168,7 @@ int main(int argc, char *argv[]) ...@@ -167,6 +168,7 @@ int main(int argc, char *argv[])
int adjfreq = 0x7fffffff; int adjfreq = 0x7fffffff;
int adjtime = 0; int adjtime = 0;
int adjns = 0; int adjns = 0;
int adjphase = 0;
int capabilities = 0; int capabilities = 0;
int extts = 0; int extts = 0;
int flagtest = 0; int flagtest = 0;
...@@ -188,7 +190,7 @@ int main(int argc, char *argv[]) ...@@ -188,7 +190,7 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/'); progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0]; progname = progname ? 1+progname : argv[0];
while (EOF != (c = getopt(argc, argv, "cd:e:f:ghH:i:k:lL:n:p:P:sSt:T:w:z"))) { while (EOF != (c = getopt(argc, argv, "cd:e:f:ghH:i:k:lL:n:o:p:P:sSt:T:w:z"))) {
switch (c) { switch (c) {
case 'c': case 'c':
capabilities = 1; capabilities = 1;
...@@ -228,6 +230,9 @@ int main(int argc, char *argv[]) ...@@ -228,6 +230,9 @@ int main(int argc, char *argv[])
case 'n': case 'n':
adjns = atoi(optarg); adjns = atoi(optarg);
break; break;
case 'o':
adjphase = atoi(optarg);
break;
case 'p': case 'p':
perout = atoll(optarg); perout = atoll(optarg);
break; break;
...@@ -287,7 +292,8 @@ int main(int argc, char *argv[]) ...@@ -287,7 +292,8 @@ int main(int argc, char *argv[])
" %d pulse per second\n" " %d pulse per second\n"
" %d programmable pins\n" " %d programmable pins\n"
" %d cross timestamping\n" " %d cross timestamping\n"
" %d adjust_phase\n", " %d adjust_phase\n"
" %d maximum phase adjustment (ns)\n",
caps.max_adj, caps.max_adj,
caps.n_alarm, caps.n_alarm,
caps.n_ext_ts, caps.n_ext_ts,
...@@ -295,7 +301,8 @@ int main(int argc, char *argv[]) ...@@ -295,7 +301,8 @@ int main(int argc, char *argv[])
caps.pps, caps.pps,
caps.n_pins, caps.n_pins,
caps.cross_timestamping, caps.cross_timestamping,
caps.adjust_phase); caps.adjust_phase,
caps.max_phase_adj);
} }
} }
...@@ -317,7 +324,7 @@ int main(int argc, char *argv[]) ...@@ -317,7 +324,7 @@ int main(int argc, char *argv[])
tx.time.tv_usec = adjns; tx.time.tv_usec = adjns;
while (tx.time.tv_usec < 0) { while (tx.time.tv_usec < 0) {
tx.time.tv_sec -= 1; tx.time.tv_sec -= 1;
tx.time.tv_usec += 1000000000; tx.time.tv_usec += NSEC_PER_SEC;
} }
if (clock_adjtime(clkid, &tx) < 0) { if (clock_adjtime(clkid, &tx) < 0) {
...@@ -327,6 +334,18 @@ int main(int argc, char *argv[]) ...@@ -327,6 +334,18 @@ int main(int argc, char *argv[])
} }
} }
if (adjphase) {
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_OFFSET | ADJ_NANO;
tx.offset = adjphase;
if (clock_adjtime(clkid, &tx) < 0) {
perror("clock_adjtime");
} else {
puts("phase adjustment okay");
}
}
if (gettime) { if (gettime) {
if (clock_gettime(clkid, &ts)) { if (clock_gettime(clkid, &ts)) {
perror("clock_gettime"); perror("clock_gettime");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment