Merge branch 'ptp-Add-adjust-phase-to-support-phase-offset'
Vincent Cheng says: ==================== ptp: Add adjust phase to support phase offset. This series adds adjust phase to the PTP Hardware Clock device interface. Some PTP hardware clocks have a write phase mode that has a built-in hardware filtering capability. The write phase mode utilizes a phase offset control word instead of a frequency offset control word. Add adjust phase function to take advantage of this capability. Changes since v1: - As suggested by Richard Cochran: 1. ops->adjphase is new so need to check for non-null function pointer. 2. Kernel coding style uses lower_case_underscores. 3. Use existing PTP clock API for delayed worker. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6c5af965c0
|
@ -136,6 +136,7 @@ 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;
|
||||||
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;
|
||||||
|
|
|
@ -146,6 +146,9 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
|
||||||
else
|
else
|
||||||
err = ops->adjfreq(ops, ppb);
|
err = ops->adjfreq(ops, ppb);
|
||||||
ptp->dialed_frequency = tx->freq;
|
ptp->dialed_frequency = tx->freq;
|
||||||
|
} else if (tx->modes & ADJ_OFFSET) {
|
||||||
|
if (ops->adjphase)
|
||||||
|
err = ops->adjphase(ops, tx->offset);
|
||||||
} else if (tx->modes == 0) {
|
} else if (tx->modes == 0) {
|
||||||
tx->freq = ptp->dialed_frequency;
|
tx->freq = ptp->dialed_frequency;
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/ptp_clock_kernel.h>
|
#include <linux/ptp_clock_kernel.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/timekeeping.h>
|
#include <linux/timekeeping.h>
|
||||||
|
|
||||||
|
@ -24,6 +25,16 @@ MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define SETTIME_CORRECTION (0)
|
#define SETTIME_CORRECTION (0)
|
||||||
|
|
||||||
|
static long set_write_phase_ready(struct ptp_clock_info *ptp)
|
||||||
|
{
|
||||||
|
struct idtcm_channel *channel =
|
||||||
|
container_of(ptp, struct idtcm_channel, caps);
|
||||||
|
|
||||||
|
channel->write_phase_ready = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int char_array_to_timespec(u8 *buf,
|
static int char_array_to_timespec(u8 *buf,
|
||||||
u8 count,
|
u8 count,
|
||||||
struct timespec64 *ts)
|
struct timespec64 *ts)
|
||||||
|
@ -871,6 +882,64 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel,
|
||||||
|
|
||||||
/* PTP Hardware Clock interface */
|
/* PTP Hardware Clock interface */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Maximum absolute value for write phase offset in picoseconds
|
||||||
|
*
|
||||||
|
* Destination signed register is 32-bit register in resolution of 50ps
|
||||||
|
*
|
||||||
|
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
|
||||||
|
*/
|
||||||
|
static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
|
||||||
|
{
|
||||||
|
struct idtcm *idtcm = channel->idtcm;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
u8 i;
|
||||||
|
u8 buf[4] = {0};
|
||||||
|
s32 phase_50ps;
|
||||||
|
s64 offset_ps;
|
||||||
|
|
||||||
|
if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
|
||||||
|
|
||||||
|
err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
channel->write_phase_ready = 0;
|
||||||
|
|
||||||
|
ptp_schedule_worker(channel->ptp_clock,
|
||||||
|
msecs_to_jiffies(WR_PHASE_SETUP_MS));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!channel->write_phase_ready)
|
||||||
|
delta_ns = 0;
|
||||||
|
|
||||||
|
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_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
buf[i] = phase_50ps & 0xff;
|
||||||
|
phase_50ps >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
|
||||||
|
buf, sizeof(buf));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int idtcm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
|
static int idtcm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
|
||||||
{
|
{
|
||||||
struct idtcm_channel *channel =
|
struct idtcm_channel *channel =
|
||||||
|
@ -977,6 +1046,24 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
|
||||||
|
{
|
||||||
|
struct idtcm_channel *channel =
|
||||||
|
container_of(ptp, struct idtcm_channel, caps);
|
||||||
|
|
||||||
|
struct idtcm *idtcm = channel->idtcm;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&idtcm->reg_lock);
|
||||||
|
|
||||||
|
err = _idtcm_adjphase(channel, delta);
|
||||||
|
|
||||||
|
mutex_unlock(&idtcm->reg_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int idtcm_enable(struct ptp_clock_info *ptp,
|
static int idtcm_enable(struct ptp_clock_info *ptp,
|
||||||
struct ptp_clock_request *rq, int on)
|
struct ptp_clock_request *rq, int on)
|
||||||
{
|
{
|
||||||
|
@ -1055,13 +1142,16 @@ static const struct ptp_clock_info idtcm_caps = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.max_adj = 244000,
|
.max_adj = 244000,
|
||||||
.n_per_out = 1,
|
.n_per_out = 1,
|
||||||
|
.adjphase = &idtcm_adjphase,
|
||||||
.adjfreq = &idtcm_adjfreq,
|
.adjfreq = &idtcm_adjfreq,
|
||||||
.adjtime = &idtcm_adjtime,
|
.adjtime = &idtcm_adjtime,
|
||||||
.gettime64 = &idtcm_gettime,
|
.gettime64 = &idtcm_gettime,
|
||||||
.settime64 = &idtcm_settime,
|
.settime64 = &idtcm_settime,
|
||||||
.enable = &idtcm_enable,
|
.enable = &idtcm_enable,
|
||||||
|
.do_aux_work = &set_write_phase_ready,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
|
static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
|
||||||
{
|
{
|
||||||
struct idtcm_channel *channel;
|
struct idtcm_channel *channel;
|
||||||
|
@ -1146,6 +1236,8 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
|
||||||
if (!channel->ptp_clock)
|
if (!channel->ptp_clock)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
channel->write_phase_ready = 0;
|
||||||
|
|
||||||
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
|
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
|
||||||
index, channel->ptp_clock->index);
|
index, channel->ptp_clock->index);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#define FW_FILENAME "idtcm.bin"
|
#define FW_FILENAME "idtcm.bin"
|
||||||
#define MAX_PHC_PLL 4
|
#define MAX_PHC_PLL 4
|
||||||
|
|
||||||
|
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
|
||||||
|
|
||||||
#define PLL_MASK_ADDR (0xFFA5)
|
#define PLL_MASK_ADDR (0xFFA5)
|
||||||
#define DEFAULT_PLL_MASK (0x04)
|
#define DEFAULT_PLL_MASK (0x04)
|
||||||
|
|
||||||
|
@ -33,8 +35,9 @@
|
||||||
|
|
||||||
#define POST_SM_RESET_DELAY_MS (3000)
|
#define POST_SM_RESET_DELAY_MS (3000)
|
||||||
#define PHASE_PULL_IN_THRESHOLD_NS (150000)
|
#define PHASE_PULL_IN_THRESHOLD_NS (150000)
|
||||||
#define TOD_WRITE_OVERHEAD_COUNT_MAX (5)
|
#define TOD_WRITE_OVERHEAD_COUNT_MAX (5)
|
||||||
#define TOD_BYTE_COUNT (11)
|
#define TOD_BYTE_COUNT (11)
|
||||||
|
#define WR_PHASE_SETUP_MS (5000)
|
||||||
|
|
||||||
/* Values of DPLL_N.DPLL_MODE.PLL_MODE */
|
/* Values of DPLL_N.DPLL_MODE.PLL_MODE */
|
||||||
enum pll_mode {
|
enum pll_mode {
|
||||||
|
@ -77,6 +80,7 @@ struct idtcm_channel {
|
||||||
u16 hw_dpll_n;
|
u16 hw_dpll_n;
|
||||||
enum pll_mode pll_mode;
|
enum pll_mode pll_mode;
|
||||||
u16 output_mask;
|
u16 output_mask;
|
||||||
|
int write_phase_ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct idtcm {
|
struct idtcm {
|
||||||
|
|
|
@ -36,7 +36,7 @@ struct ptp_system_timestamp {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ptp_clock_info - decribes a PTP hardware clock
|
* struct ptp_clock_info - describes a PTP hardware clock
|
||||||
*
|
*
|
||||||
* @owner: The clock driver should set to THIS_MODULE.
|
* @owner: The clock driver should set to THIS_MODULE.
|
||||||
* @name: A short "friendly name" to identify the clock and to
|
* @name: A short "friendly name" to identify the clock and to
|
||||||
|
@ -65,6 +65,9 @@ struct ptp_system_timestamp {
|
||||||
* parameter delta: Desired frequency offset from nominal frequency
|
* parameter delta: Desired frequency offset from nominal frequency
|
||||||
* in parts per billion
|
* in parts per billion
|
||||||
*
|
*
|
||||||
|
* @adjphase: Adjusts the phase offset of the hardware clock.
|
||||||
|
* parameter delta: Desired change in nanoseconds.
|
||||||
|
*
|
||||||
* @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.
|
||||||
*
|
*
|
||||||
|
@ -128,6 +131,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 (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
|
int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
|
||||||
|
int (*adjphase)(struct ptp_clock_info *ptp, s32 phase);
|
||||||
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,
|
||||||
|
|
|
@ -89,7 +89,9 @@ struct ptp_clock_caps {
|
||||||
int n_pins; /* Number of input/output pins. */
|
int n_pins; /* Number of input/output pins. */
|
||||||
/* Whether the clock supports precise system-device cross timestamps */
|
/* Whether the clock supports precise system-device cross timestamps */
|
||||||
int cross_timestamping;
|
int cross_timestamping;
|
||||||
int rsv[13]; /* Reserved for future use. */
|
/* Whether the clock supports adjust phase */
|
||||||
|
int adjust_phase;
|
||||||
|
int rsv[12]; /* Reserved for future use. */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ptp_extts_request {
|
struct ptp_extts_request {
|
||||||
|
|
|
@ -269,14 +269,16 @@ int main(int argc, char *argv[])
|
||||||
" %d programmable periodic signals\n"
|
" %d programmable periodic signals\n"
|
||||||
" %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",
|
||||||
caps.max_adj,
|
caps.max_adj,
|
||||||
caps.n_alarm,
|
caps.n_alarm,
|
||||||
caps.n_ext_ts,
|
caps.n_ext_ts,
|
||||||
caps.n_per_out,
|
caps.n_per_out,
|
||||||
caps.pps,
|
caps.pps,
|
||||||
caps.n_pins,
|
caps.n_pins,
|
||||||
caps.cross_timestamping);
|
caps.cross_timestamping,
|
||||||
|
caps.adjust_phase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue