Skip to content

Commit

Permalink
Merge branch 'ptp-adjphase-cleanups'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 20, 2023
2 parents 4931062 + d8ee5ca commit 712557f
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 56 deletions.
29 changes: 29 additions & 0 deletions Documentation/driver-api/ptp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ Writing clock drivers
class driver, since the lock may also be needed by the clock
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
==================

Expand Down Expand Up @@ -106,3 +122,16 @@ Supported hardware
- 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)
- 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
31 changes: 15 additions & 16 deletions drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,23 @@ static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev)
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;
s64 max = MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX;
struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
struct mlx5_core_dev *mdev;

if (MLX5_CAP_MCAM_FEATURE(mdev, mtutc_time_adjustment_extended_range)) {
min = MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MIN;
max = MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX;
}
mdev = container_of(clock, struct mlx5_core_dev, clock);

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 true;
Expand Down Expand Up @@ -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)
{
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);
}

Expand Down Expand Up @@ -734,6 +732,7 @@ static const struct ptp_clock_info mlx5_ptp_clock_info = {
.pps = 0,
.adjfine = mlx5_ptp_adjfine,
.adjphase = mlx5_ptp_adjphase,
.getmaxphase = mlx5_ptp_getmaxphase,
.adjtime = mlx5_ptp_adjtime,
.gettimex64 = mlx5_ptp_gettimex,
.settime64 = mlx5_ptp_settime,
Expand Down
5 changes: 4 additions & 1 deletion drivers/ptp/ptp_chardev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions drivers/ptp/ptp_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
36 changes: 17 additions & 19 deletions drivers/ptp/ptp_clockmatrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -1692,14 +1692,23 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel)
/* PTP Hardware Clock interface */

/*
* Maximum absolute value for write phase offset in picoseconds
*
* @channel: channel
* @delta_ns: delta in nanoseconds
* Maximum absolute value for write phase offset in nanoseconds
*
* 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)
{
Expand All @@ -1708,27 +1717,14 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
u8 i;
u8 buf[4] = {0};
s32 phase_50ps;
s64 offset_ps;

if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) {
err = channel->configure_write_phase(channel);
if (err)
return err;
}

offset_ps = (s64)delta_ns * 1000;

/*
* 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);
phase_50ps = div_s64((s64)delta_ns * 1000, 50);

for (i = 0; i < 4; i++) {
buf[i] = phase_50ps & 0xff;
Expand Down Expand Up @@ -2048,6 +2044,7 @@ static const struct ptp_clock_info idtcm_caps = {
.n_ext_ts = MAX_TOD,
.n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase,
.getmaxphase = &idtcm_getmaxphase,
.adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime,
Expand All @@ -2064,6 +2061,7 @@ static const struct ptp_clock_info idtcm_caps_deprecated = {
.n_ext_ts = MAX_TOD,
.n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase,
.getmaxphase = &idtcm_getmaxphase,
.adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime_deprecated,
.gettime64 = &idtcm_gettime,
Expand Down
2 changes: 1 addition & 1 deletion drivers/ptp/ptp_clockmatrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#define MAX_PLL (8)
#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 DEFAULT_TOD_MASK (0x04)
Expand Down
18 changes: 9 additions & 9 deletions drivers/ptp/ptp_idt82p33.c
Original file line number Diff line number Diff line change
Expand Up @@ -978,24 +978,23 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
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)
{
struct idt82p33_channel *channel =
container_of(ptp, struct idt82p33_channel, caps);
struct idt82p33 *idt82p33 = channel->idt82p33;
s64 offset_regval, offset_fs;
s64 offset_regval;
u8 val[4] = {0};
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 */
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[1] = (offset_regval >> 8) & 0xFF;
Expand Down Expand Up @@ -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_pins = max_pins,
caps->adjphase = idt82p33_adjwritephase,
caps->getmaxphase = idt82p33_getmaxphase,
caps->adjfine = idt82p33_adjfine;
caps->adjtime = idt82p33_adjtime;
caps->gettime64 = idt82p33_gettime;
Expand Down
4 changes: 2 additions & 2 deletions drivers/ptp/ptp_idt82p33.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
#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
*
Expand Down
7 changes: 7 additions & 0 deletions drivers/ptp/ptp_ocp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,12 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
return -EOPNOTSUPP;
}

static s32
ptp_ocp_null_getmaxphase(struct ptp_clock_info *ptp_info)
{
return 0;
}

static int
ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns)
{
Expand Down Expand Up @@ -1239,6 +1245,7 @@ static const struct ptp_clock_info ptp_ocp_clock_info = {
.adjtime = ptp_ocp_adjtime,
.adjfine = ptp_ocp_null_adjfine,
.adjphase = ptp_ocp_null_adjphase,
.getmaxphase = ptp_ocp_null_getmaxphase,
.enable = ptp_ocp_enable,
.verify = ptp_ocp_verify,
.pps = true,
Expand Down
12 changes: 12 additions & 0 deletions drivers/ptp/ptp_sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 9 additions & 2 deletions include/linux/ptp_clock_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,14 @@ struct ptp_system_timestamp {
* nominal frequency in parts per million, but with a
* 16 bit binary fractional field.
*
* @adjphase: Adjusts the phase offset of the hardware clock.
* parameter delta: Desired change in nanoseconds.
* @adjphase: Indicates that the PHC should use an internal servo
* 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.
* parameter delta: Desired change in nanoseconds.
Expand Down Expand Up @@ -169,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,
Expand Down
3 changes: 2 additions & 1 deletion include/uapi/linux/ptp_clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 712557f

Please sign in to comment.