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

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
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -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
==================

@@ -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
+15 −16
Original line number Diff line number Diff line
@@ -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;
}

	if (delta < min || delta > 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 < -max || delta > max)
		return false;

	return true;
@@ -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);
}

@@ -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,
+4 −1
Original line number Diff line number Diff line
@@ -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;
+4 −0
Original line number Diff line number Diff line
@@ -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) {
+17 −19
Original line number Diff line number Diff line
@@ -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)
{
@@ -1708,7 +1717,6 @@ 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);
@@ -1716,19 +1724,7 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
			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;
@@ -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,
@@ -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,
Loading