ptp: ptp_clockmatrix: Add adjphase() to support PHC write phase mode.

Add idtcm_adjphase() to support PHC write phase mode.

Signed-off-by: Vincent Cheng <vincent.cheng.xh@renesas.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vincent Cheng 2020-05-01 23:35:38 -04:00 committed by David S. Miller
parent d3f1cbd29f
commit 425d2b1c56
2 changed files with 98 additions and 2 deletions

View File

@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/timekeeping.h>
@ -24,6 +25,16 @@ MODULE_LICENSE("GPL");
#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,
u8 count,
struct timespec64 *ts)
@ -871,6 +882,64 @@ 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 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)
{
struct idtcm_channel *channel =
@ -977,6 +1046,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,13 +1142,16 @@ 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,
.settime64 = &idtcm_settime,
.enable = &idtcm_enable,
.do_aux_work = &set_write_phase_ready,
};
static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
{
struct idtcm_channel *channel;
@ -1146,6 +1236,8 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
if (!channel->ptp_clock)
return -ENOTSUPP;
channel->write_phase_ready = 0;
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
index, channel->ptp_clock->index);

View File

@ -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,7 @@ struct idtcm_channel {
u16 hw_dpll_n;
enum pll_mode pll_mode;
u16 output_mask;
int write_phase_ready;
};
struct idtcm {