2020-04-30 00:39:00

by Vincent Cheng

[permalink] [raw]
Subject: [PATCH net-next 0/3] ptp: Add adjust phase to support phase offset.

From: Vincent Cheng <[email protected]>

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.

Vincent Cheng (3):
ptp: Add adjphase function to support phase offset control.
ptp: Add adjust_phase to ptp_clock_caps capability.
ptp: ptp_clockmatrix: Add adjphase() to support PHC write phase mode.

drivers/ptp/ptp_chardev.c | 1 +
drivers/ptp/ptp_clock.c | 2 +
drivers/ptp/ptp_clockmatrix.c | 123 ++++++++++++++++++++++++++++++++++
drivers/ptp/ptp_clockmatrix.h | 11 ++-
include/linux/ptp_clock_kernel.h | 6 +-
include/uapi/linux/ptp_clock.h | 4 +-
tools/testing/selftests/ptp/testptp.c | 6 +-
7 files changed, 147 insertions(+), 6 deletions(-)

--
2.7.4


2020-04-30 00:41:56

by Vincent Cheng

[permalink] [raw]
Subject: [PATCH net-next 2/3] ptp: Add adjust_phase to ptp_clock_caps capability.

From: Vincent Cheng <[email protected]>

Add adjust_phase to ptp_clock_caps capability to allow
user to query if a PHC driver supports adjust phase with
ioctl PTP_CLOCK_GETCAPS command.

Signed-off-by: Vincent Cheng <[email protected]>
---
drivers/ptp/ptp_chardev.c | 1 +
include/uapi/linux/ptp_clock.h | 4 +++-
tools/testing/selftests/ptp/testptp.c | 6 ++++--
3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 93d574f..375cd6e 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -136,6 +136,7 @@ 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;
if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
err = -EFAULT;
break;
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index 9dc9d00..ff070aa 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -89,7 +89,9 @@ struct ptp_clock_caps {
int n_pins; /* Number of input/output pins. */
/* Whether the clock supports precise system-device cross timestamps */
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 {
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
index c0dd102..da7a9dd 100644
--- a/tools/testing/selftests/ptp/testptp.c
+++ b/tools/testing/selftests/ptp/testptp.c
@@ -269,14 +269,16 @@ int main(int argc, char *argv[])
" %d programmable periodic signals\n"
" %d pulse per second\n"
" %d programmable pins\n"
- " %d cross timestamping\n",
+ " %d cross timestamping\n"
+ " %d adjust_phase\n",
caps.max_adj,
caps.n_alarm,
caps.n_ext_ts,
caps.n_per_out,
caps.pps,
caps.n_pins,
- caps.cross_timestamping);
+ caps.cross_timestamping,
+ caps.adjust_phase);
}
}

--
2.7.4

2020-04-30 00:47:00

by Vincent Cheng

[permalink] [raw]
Subject: [PATCH net-next 1/3] ptp: Add adjphase function to support phase offset control.

From: Vincent Cheng <[email protected]>

Adds adjust phase function to take advantage of a PHC
clock's hardware filtering capability that uses phase offset
control word instead of frequency offset control word.

Signed-off-by: Vincent Cheng <[email protected]>
---
drivers/ptp/ptp_clock.c | 2 ++
include/linux/ptp_clock_kernel.h | 6 +++++-
2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index acabbe7..c46ff98 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -146,6 +146,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
else
err = ops->adjfreq(ops, ppb);
ptp->dialed_frequency = tx->freq;
+ } else if (tx->modes & ADJ_OFFSET) {
+ err = ops->adjphase(ops, tx->offset);
} else if (tx->modes == 0) {
tx->freq = ptp->dialed_frequency;
err = 0;
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 121a7ed..31144d9 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -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.
* @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
* 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.
* parameter delta: Desired change in nanoseconds.
*
@@ -128,6 +131,7 @@ struct ptp_clock_info {
struct ptp_pin_desc *pin_config;
int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);
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 (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
--
2.7.4

2020-04-30 01:06:46

by Vincent Cheng

[permalink] [raw]
Subject: [PATCH net-next 3/3] ptp: ptp_clockmatrix: Add adjphase() to support PHC write phase mode.

From: Vincent Cheng <[email protected]>

Add idtcm_adjphase() to support PHC write phase mode.

Signed-off-by: Vincent Cheng <[email protected]>
---
drivers/ptp/ptp_clockmatrix.c | 123 ++++++++++++++++++++++++++++++++++++++++++
drivers/ptp/ptp_clockmatrix.h | 11 +++-
2 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index a3f6088..07b13c3 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -24,6 +24,15 @@ MODULE_LICENSE("GPL");

#define SETTIME_CORRECTION (0)

+static void set_write_phase_ready(struct kthread_work *work)
+{
+ struct idtcm_channel *ch = container_of(work,
+ struct idtcm_channel,
+ write_phase_ready_work.work);
+
+ ch->write_phase_ready = 1;
+}
+
static int char_array_to_timespec(u8 *buf,
u8 count,
struct timespec64 *ts)
@@ -871,6 +880,69 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel,

/* 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 deltaNs)
+{
+ struct idtcm *idtcm = channel->idtcm;
+
+ int err;
+ u8 i;
+ u8 buf[4] = {0};
+ s32 phaseIn50Picoseconds;
+ s64 phaseOffsetInPs;
+
+ if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
+
+ kthread_cancel_delayed_work_sync(
+ &channel->write_phase_ready_work);
+
+ err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
+
+ if (err)
+ return err;
+
+ channel->write_phase_ready = 0;
+
+ kthread_queue_delayed_work(channel->kworker,
+ &channel->write_phase_ready_work,
+ msecs_to_jiffies(WR_PHASE_SETUP_MS));
+ }
+
+ if (!channel->write_phase_ready)
+ deltaNs = 0;
+
+ phaseOffsetInPs = (s64)deltaNs * 1000;
+
+ /*
+ * Check for 32-bit signed max * 50:
+ *
+ * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
+ */
+ if (phaseOffsetInPs > MAX_ABS_WRITE_PHASE_PICOSECONDS)
+ phaseOffsetInPs = MAX_ABS_WRITE_PHASE_PICOSECONDS;
+ else if (phaseOffsetInPs < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
+ phaseOffsetInPs = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
+
+ phaseIn50Picoseconds = DIV_ROUND_CLOSEST(div64_s64(phaseOffsetInPs, 50),
+ 1);
+
+ for (i = 0; i < 4; i++) {
+ buf[i] = phaseIn50Picoseconds & 0xff;
+ phaseIn50Picoseconds >>= 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)
{
struct idtcm_channel *channel =
@@ -977,6 +1049,24 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
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,
struct ptp_clock_request *rq, int on)
{
@@ -1055,6 +1145,7 @@ static const struct ptp_clock_info idtcm_caps = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 1,
+ .adjphase = &idtcm_adjphase,
.adjfreq = &idtcm_adjfreq,
.adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime,
@@ -1062,6 +1153,21 @@ static const struct ptp_clock_info idtcm_caps = {
.enable = &idtcm_enable,
};

+static int write_phase_worker_setup(struct idtcm_channel *channel, int index)
+{
+ channel->kworker = kthread_create_worker(0, "channel%d", index);
+
+ if (IS_ERR(channel->kworker))
+ return PTR_ERR(channel->kworker);
+
+ channel->write_phase_ready = 0;
+
+ kthread_init_delayed_work(&channel->write_phase_ready_work,
+ set_write_phase_ready);
+
+ return 0;
+}
+
static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
{
struct idtcm_channel *channel;
@@ -1146,6 +1252,10 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
if (!channel->ptp_clock)
return -ENOTSUPP;

+ err = write_phase_worker_setup(channel, index);
+ if (err)
+ return err;
+
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
index, channel->ptp_clock->index);

@@ -1284,6 +1394,19 @@ static int idtcm_remove(struct i2c_client *client)
{
struct idtcm *idtcm = i2c_get_clientdata(client);

+ int i;
+ struct idtcm_channel *channel;
+
+ for (i = 0; i < MAX_PHC_PLL; i++) {
+ channel = &idtcm->channel[i];
+
+ if (channel->kworker) {
+ kthread_cancel_delayed_work_sync(
+ &channel->write_phase_ready_work);
+ kthread_destroy_worker(channel->kworker);
+ }
+ }
+
ptp_clock_unregister_all(idtcm);

mutex_destroy(&idtcm->reg_lock);
diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 6c1f93a..36e133d 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -15,6 +15,8 @@
#define FW_FILENAME "idtcm.bin"
#define MAX_PHC_PLL 4

+#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
+
#define PLL_MASK_ADDR (0xFFA5)
#define DEFAULT_PLL_MASK (0x04)

@@ -33,8 +35,9 @@

#define POST_SM_RESET_DELAY_MS (3000)
#define PHASE_PULL_IN_THRESHOLD_NS (150000)
-#define TOD_WRITE_OVERHEAD_COUNT_MAX (5)
-#define TOD_BYTE_COUNT (11)
+#define TOD_WRITE_OVERHEAD_COUNT_MAX (5)
+#define TOD_BYTE_COUNT (11)
+#define WR_PHASE_SETUP_MS (5000)

/* Values of DPLL_N.DPLL_MODE.PLL_MODE */
enum pll_mode {
@@ -77,6 +80,10 @@ struct idtcm_channel {
u16 hw_dpll_n;
enum pll_mode pll_mode;
u16 output_mask;
+ int write_phase_ready;
+
+ struct kthread_worker *kworker;
+ struct kthread_delayed_work write_phase_ready_work;
};

struct idtcm {
--
2.7.4

2020-05-01 03:39:32

by Richard Cochran

[permalink] [raw]
Subject: Re: [PATCH net-next 1/3] ptp: Add adjphase function to support phase offset control.

On Wed, Apr 29, 2020 at 08:28:23PM -0400, [email protected] wrote:
> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> index acabbe7..c46ff98 100644
> --- a/drivers/ptp/ptp_clock.c
> +++ b/drivers/ptp/ptp_clock.c
> @@ -146,6 +146,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
> else
> err = ops->adjfreq(ops, ppb);
> ptp->dialed_frequency = tx->freq;
> + } else if (tx->modes & ADJ_OFFSET) {
> + err = ops->adjphase(ops, tx->offset);

This is a new method, and no drivers have it, so there must be a check
that the function pointer is non-null.

Thanks,
Richard

2020-05-01 03:40:42

by Richard Cochran

[permalink] [raw]
Subject: Re: [PATCH net-next 2/3] ptp: Add adjust_phase to ptp_clock_caps capability.

On Wed, Apr 29, 2020 at 08:28:24PM -0400, [email protected] wrote:
> From: Vincent Cheng <[email protected]>
>
> Add adjust_phase to ptp_clock_caps capability to allow
> user to query if a PHC driver supports adjust phase with
> ioctl PTP_CLOCK_GETCAPS command.
>
> Signed-off-by: Vincent Cheng <[email protected]>

Reviewed-by: Richard Cochran <[email protected]>

2020-05-01 03:57:55

by Richard Cochran

[permalink] [raw]
Subject: Re: [PATCH net-next 3/3] ptp: ptp_clockmatrix: Add adjphase() to support PHC write phase mode.

On Wed, Apr 29, 2020 at 08:28:25PM -0400, [email protected] wrote:
> @@ -871,6 +880,69 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel,
>
> /* 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 deltaNs)
> +{
> + struct idtcm *idtcm = channel->idtcm;
> +
> + int err;
> + u8 i;
> + u8 buf[4] = {0};
> + s32 phaseIn50Picoseconds;
> + s64 phaseOffsetInPs;

Kernel coding style uses lower_case_underscores rather than lowerCamelCase.

> +
> + if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
> +
> + kthread_cancel_delayed_work_sync(
> + &channel->write_phase_ready_work);
> +
> + err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
> +
> + if (err)
> + return err;
> +
> + channel->write_phase_ready = 0;
> +
> + kthread_queue_delayed_work(channel->kworker,
> + &channel->write_phase_ready_work,
> + msecs_to_jiffies(WR_PHASE_SETUP_MS));

Each PHC driver automatically has a kworker provided by the class
layer. In order to use it, set ptp_clock_info.do_aux_work to your
callback function and then call ptp_schedule_worker() when needed.

See drivers/net/ethernet/ti/cpts.c for example.

Thanks,
Richard

2020-05-01 13:50:39

by Vincent Cheng

[permalink] [raw]
Subject: Re: [PATCH net-next 1/3] ptp: Add adjphase function to support phase offset control.

On Thu, Apr 30, 2020 at 11:37:34PM EDT, Richard Cochran wrote:
>On Wed, Apr 29, 2020 at 08:28:23PM -0400, [email protected] wrote:
>> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
>> index acabbe7..c46ff98 100644
>> --- a/drivers/ptp/ptp_clock.c
>> +++ b/drivers/ptp/ptp_clock.c
>> @@ -146,6 +146,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
>> else
>> err = ops->adjfreq(ops, ppb);
>> ptp->dialed_frequency = tx->freq;
>> + } else if (tx->modes & ADJ_OFFSET) {
>> + err = ops->adjphase(ops, tx->offset);
>
>This is a new method, and no drivers have it, so there must be a check
>that the function pointer is non-null.

Yes, good point. Will fix and resubmit.

Thanks,
Vincent

2020-05-01 13:54:33

by Vincent Cheng

[permalink] [raw]
Subject: Re: [PATCH net-next 3/3] ptp: ptp_clockmatrix: Add adjphase() to support PHC write phase mode.

On Thu, Apr 30, 2020 at 11:56:01PM EDT, Richard Cochran wrote:
>On Wed, Apr 29, 2020 at 08:28:25PM -0400, [email protected] wrote:
>> @@ -871,6 +880,69 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel,
>>
>> + int err;
>> + u8 i;
>> + u8 buf[4] = {0};
>> + s32 phaseIn50Picoseconds;
>> + s64 phaseOffsetInPs;
>
>Kernel coding style uses lower_case_underscores rather than lowerCamelCase.

Sorry, missed that. Will fix.

>> + kthread_queue_delayed_work(channel->kworker,
>> + &channel->write_phase_ready_work,
>> + msecs_to_jiffies(WR_PHASE_SETUP_MS));
>
>Each PHC driver automatically has a kworker provided by the class
>layer. In order to use it, set ptp_clock_info.do_aux_work to your
>callback function and then call ptp_schedule_worker() when needed.
>
>See drivers/net/ethernet/ti/cpts.c for example.

That is nice of the API, thank-you for the example. Will fix and re-submit.

Thanks,
Vincent