forked from Minki/linux
ptp: ptp_clockmatrix: Add PTP_CLK_REQ_EXTTS support
Use TOD_READ_SECONDARY for extts to keep TOD_READ_PRIMARY for gettime and settime exclusively. Before this change, TOD_READ_PRIMARY was used for both extts and gettime/settime, which would result in changing TOD read/write triggers between operations. Using TOD_READ_SECONDARY would make extts independent of gettime/settime operation Signed-off-by: Min Li <min.li.xe@renesas.com> Acked-by: Richard Cochran <richardcochran@gmail.com> Link: https://lore.kernel.org/r/1652712427-14703-1-git-send-email-min.li.xe@renesas.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
5ff0348b7f
commit
bec6759252
@ -239,73 +239,97 @@ static int wait_for_boot_status_ready(struct idtcm *idtcm)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _idtcm_set_scsr_read_trig(struct idtcm_channel *channel,
|
static int arm_tod_read_trig_sel_refclk(struct idtcm_channel *channel, u8 ref)
|
||||||
enum scsr_read_trig_sel trig, u8 ref)
|
|
||||||
{
|
{
|
||||||
struct idtcm *idtcm = channel->idtcm;
|
struct idtcm *idtcm = channel->idtcm;
|
||||||
u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
|
u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
|
||||||
u8 val;
|
u8 val = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (trig == SCSR_TOD_READ_TRIG_SEL_REFCLK) {
|
val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
|
||||||
err = idtcm_read(idtcm, channel->tod_read_primary,
|
val |= (ref << WR_REF_INDEX_SHIFT);
|
||||||
TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
|
err = idtcm_write(idtcm, channel->tod_read_secondary,
|
||||||
val |= (ref << WR_REF_INDEX_SHIFT);
|
TOD_READ_SECONDARY_SEL_CFG_0, &val, sizeof(val));
|
||||||
|
|
||||||
err = idtcm_write(idtcm, channel->tod_read_primary,
|
|
||||||
TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = idtcm_read(idtcm, channel->tod_read_primary,
|
|
||||||
tod_read_cmd, &val, sizeof(val));
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
val &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
|
val = 0 | (SCSR_TOD_READ_TRIG_SEL_REFCLK << TOD_READ_TRIGGER_SHIFT);
|
||||||
val |= (trig << TOD_READ_TRIGGER_SHIFT);
|
|
||||||
val &= ~TOD_READ_TRIGGER_MODE; /* single shot */
|
err = idtcm_write(idtcm, channel->tod_read_secondary, tod_read_cmd,
|
||||||
|
&val, sizeof(val));
|
||||||
|
if (err)
|
||||||
|
dev_err(idtcm->dev, "%s: err = %d", __func__, err);
|
||||||
|
|
||||||
err = idtcm_write(idtcm, channel->tod_read_primary,
|
|
||||||
tod_read_cmd, &val, sizeof(val));
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int idtcm_enable_extts(struct idtcm_channel *channel, u8 todn, u8 ref,
|
static bool is_single_shot(u8 mask)
|
||||||
bool enable)
|
|
||||||
{
|
{
|
||||||
struct idtcm *idtcm = channel->idtcm;
|
/* Treat single bit ToD masks as continuous trigger */
|
||||||
u8 old_mask = idtcm->extts_mask;
|
return mask <= 8 && is_power_of_2(mask);
|
||||||
u8 mask = 1 << todn;
|
}
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if (todn >= MAX_TOD)
|
static int idtcm_extts_enable(struct idtcm_channel *channel,
|
||||||
|
struct ptp_clock_request *rq, int on)
|
||||||
|
{
|
||||||
|
u8 index = rq->extts.index;
|
||||||
|
struct idtcm *idtcm;
|
||||||
|
u8 mask = 1 << index;
|
||||||
|
int err = 0;
|
||||||
|
u8 old_mask;
|
||||||
|
int ref;
|
||||||
|
|
||||||
|
idtcm = channel->idtcm;
|
||||||
|
old_mask = idtcm->extts_mask;
|
||||||
|
|
||||||
|
/* Reject requests with unsupported flags */
|
||||||
|
if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
|
||||||
|
PTP_RISING_EDGE |
|
||||||
|
PTP_FALLING_EDGE |
|
||||||
|
PTP_STRICT_FLAGS))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* Reject requests to enable time stamping on falling edge */
|
||||||
|
if ((rq->extts.flags & PTP_ENABLE_FEATURE) &&
|
||||||
|
(rq->extts.flags & PTP_FALLING_EDGE))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (index >= MAX_TOD)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (enable) {
|
if (on) {
|
||||||
if (ref > 0xF) /* E_REF_CLK15 */
|
/* Support triggering more than one TOD_0/1/2/3 by same pin */
|
||||||
return -EINVAL;
|
/* Use the pin configured for the channel */
|
||||||
if (idtcm->extts_mask & mask)
|
ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->tod);
|
||||||
return 0;
|
|
||||||
err = _idtcm_set_scsr_read_trig(&idtcm->channel[todn],
|
if (ref < 0) {
|
||||||
SCSR_TOD_READ_TRIG_SEL_REFCLK,
|
dev_err(idtcm->dev, "%s: No valid pin found for TOD%d!\n",
|
||||||
ref);
|
__func__, channel->tod);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = arm_tod_read_trig_sel_refclk(&idtcm->channel[index], ref);
|
||||||
|
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
idtcm->extts_mask |= mask;
|
idtcm->extts_mask |= mask;
|
||||||
idtcm->event_channel[todn] = channel;
|
idtcm->event_channel[index] = channel;
|
||||||
idtcm->channel[todn].refn = ref;
|
idtcm->channel[index].refn = ref;
|
||||||
}
|
idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
|
||||||
} else
|
|
||||||
idtcm->extts_mask &= ~mask;
|
|
||||||
|
|
||||||
if (old_mask == 0 && idtcm->extts_mask)
|
if (old_mask)
|
||||||
schedule_delayed_work(&idtcm->extts_work,
|
return 0;
|
||||||
msecs_to_jiffies(EXTTS_PERIOD_MS));
|
|
||||||
|
schedule_delayed_work(&idtcm->extts_work,
|
||||||
|
msecs_to_jiffies(EXTTS_PERIOD_MS));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idtcm->extts_mask &= ~mask;
|
||||||
|
idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
|
||||||
|
|
||||||
|
if (idtcm->extts_mask == 0)
|
||||||
|
cancel_delayed_work(&idtcm->extts_work);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -371,6 +395,31 @@ static void wait_for_chip_ready(struct idtcm *idtcm)
|
|||||||
"Continuing while SYS APLL/DPLL is not locked");
|
"Continuing while SYS APLL/DPLL is not locked");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _idtcm_gettime_triggered(struct idtcm_channel *channel,
|
||||||
|
struct timespec64 *ts)
|
||||||
|
{
|
||||||
|
struct idtcm *idtcm = channel->idtcm;
|
||||||
|
u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
|
||||||
|
u8 buf[TOD_BYTE_COUNT];
|
||||||
|
u8 trigger;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = idtcm_read(idtcm, channel->tod_read_secondary,
|
||||||
|
tod_read_cmd, &trigger, sizeof(trigger));
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (trigger & TOD_READ_TRIGGER_MASK)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
err = idtcm_read(idtcm, channel->tod_read_secondary,
|
||||||
|
TOD_READ_SECONDARY_BASE, buf, sizeof(buf));
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return char_array_to_timespec(buf, sizeof(buf), ts);
|
||||||
|
}
|
||||||
|
|
||||||
static int _idtcm_gettime(struct idtcm_channel *channel,
|
static int _idtcm_gettime(struct idtcm_channel *channel,
|
||||||
struct timespec64 *ts, u8 timeout)
|
struct timespec64 *ts, u8 timeout)
|
||||||
{
|
{
|
||||||
@ -396,7 +445,7 @@ static int _idtcm_gettime(struct idtcm_channel *channel,
|
|||||||
} while (trigger & TOD_READ_TRIGGER_MASK);
|
} while (trigger & TOD_READ_TRIGGER_MASK);
|
||||||
|
|
||||||
err = idtcm_read(idtcm, channel->tod_read_primary,
|
err = idtcm_read(idtcm, channel->tod_read_primary,
|
||||||
TOD_READ_PRIMARY, buf, sizeof(buf));
|
TOD_READ_PRIMARY_BASE, buf, sizeof(buf));
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -415,67 +464,38 @@ static int idtcm_extts_check_channel(struct idtcm *idtcm, u8 todn)
|
|||||||
|
|
||||||
extts_channel = &idtcm->channel[todn];
|
extts_channel = &idtcm->channel[todn];
|
||||||
ptp_channel = idtcm->event_channel[todn];
|
ptp_channel = idtcm->event_channel[todn];
|
||||||
|
|
||||||
if (extts_channel == ptp_channel)
|
if (extts_channel == ptp_channel)
|
||||||
dco_delay = ptp_channel->dco_delay;
|
dco_delay = ptp_channel->dco_delay;
|
||||||
|
|
||||||
err = _idtcm_gettime(extts_channel, &ts, 1);
|
err = _idtcm_gettime_triggered(extts_channel, &ts);
|
||||||
if (err == 0) {
|
if (err)
|
||||||
event.type = PTP_CLOCK_EXTTS;
|
return err;
|
||||||
event.index = todn;
|
|
||||||
event.timestamp = timespec64_to_ns(&ts) - dco_delay;
|
/* Triggered - save timestamp */
|
||||||
ptp_clock_event(ptp_channel->ptp_clock, &event);
|
event.type = PTP_CLOCK_EXTTS;
|
||||||
}
|
event.index = todn;
|
||||||
|
event.timestamp = timespec64_to_ns(&ts) - dco_delay;
|
||||||
|
ptp_clock_event(ptp_channel->ptp_clock, &event);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 idtcm_enable_extts_mask(struct idtcm_channel *channel,
|
|
||||||
u8 extts_mask, bool enable)
|
|
||||||
{
|
|
||||||
struct idtcm *idtcm = channel->idtcm;
|
|
||||||
int i, err;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_TOD; i++) {
|
|
||||||
u8 mask = 1 << i;
|
|
||||||
u8 refn = idtcm->channel[i].refn;
|
|
||||||
|
|
||||||
if (extts_mask & mask) {
|
|
||||||
/* check extts before disabling it */
|
|
||||||
if (enable == false) {
|
|
||||||
err = idtcm_extts_check_channel(idtcm, i);
|
|
||||||
/* trigger happened so we won't re-enable it */
|
|
||||||
if (err == 0)
|
|
||||||
extts_mask &= ~mask;
|
|
||||||
}
|
|
||||||
(void)idtcm_enable_extts(channel, i, refn, enable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extts_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _idtcm_gettime_immediate(struct idtcm_channel *channel,
|
static int _idtcm_gettime_immediate(struct idtcm_channel *channel,
|
||||||
struct timespec64 *ts)
|
struct timespec64 *ts)
|
||||||
{
|
{
|
||||||
struct idtcm *idtcm = channel->idtcm;
|
struct idtcm *idtcm = channel->idtcm;
|
||||||
u8 extts_mask = 0;
|
|
||||||
|
u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
|
||||||
|
u8 val = (SCSR_TOD_READ_TRIG_SEL_IMMEDIATE << TOD_READ_TRIGGER_SHIFT);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Disable extts */
|
err = idtcm_write(idtcm, channel->tod_read_primary,
|
||||||
if (idtcm->extts_mask) {
|
tod_read_cmd, &val, sizeof(val));
|
||||||
extts_mask = idtcm_enable_extts_mask(channel, idtcm->extts_mask,
|
if (err)
|
||||||
false);
|
return err;
|
||||||
}
|
|
||||||
|
|
||||||
err = _idtcm_set_scsr_read_trig(channel,
|
return _idtcm_gettime(channel, ts, 10);
|
||||||
SCSR_TOD_READ_TRIG_SEL_IMMEDIATE, 0);
|
|
||||||
if (err == 0)
|
|
||||||
err = _idtcm_gettime(channel, ts, 10);
|
|
||||||
|
|
||||||
/* Re-enable extts */
|
|
||||||
if (extts_mask)
|
|
||||||
idtcm_enable_extts_mask(channel, extts_mask, true);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _sync_pll_output(struct idtcm *idtcm,
|
static int _sync_pll_output(struct idtcm *idtcm,
|
||||||
@ -1702,6 +1722,9 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel)
|
|||||||
/*
|
/*
|
||||||
* Maximum absolute value for write phase offset in picoseconds
|
* Maximum absolute value for write phase offset in picoseconds
|
||||||
*
|
*
|
||||||
|
* @channel: channel
|
||||||
|
* @delta_ns: delta in nanoseconds
|
||||||
|
*
|
||||||
* Destination signed register is 32-bit register in resolution of 50ps
|
* Destination signed register is 32-bit register in resolution of 50ps
|
||||||
*
|
*
|
||||||
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
|
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
|
||||||
@ -1958,8 +1981,7 @@ static int idtcm_enable(struct ptp_clock_info *ptp,
|
|||||||
err = idtcm_perout_enable(channel, &rq->perout, true);
|
err = idtcm_perout_enable(channel, &rq->perout, true);
|
||||||
break;
|
break;
|
||||||
case PTP_CLK_REQ_EXTTS:
|
case PTP_CLK_REQ_EXTTS:
|
||||||
err = idtcm_enable_extts(channel, rq->extts.index,
|
err = idtcm_extts_enable(channel, rq, on);
|
||||||
rq->extts.rsv[0], on);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1982,13 +2004,6 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
|
|||||||
u8 cfg;
|
u8 cfg;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* STEELAI-366 - Temporary workaround for ts2phc compatibility */
|
|
||||||
if (0) {
|
|
||||||
err = idtcm_output_mask_enable(channel, false);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the TOD clock ticking.
|
* Start the TOD clock ticking.
|
||||||
*/
|
*/
|
||||||
@ -2038,17 +2053,35 @@ static void idtcm_set_version_info(struct idtcm *idtcm)
|
|||||||
product_id, hw_rev_id, config_select);
|
product_id, hw_rev_id, config_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int idtcm_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
|
||||||
|
enum ptp_pin_function func, unsigned int chan)
|
||||||
|
{
|
||||||
|
switch (func) {
|
||||||
|
case PTP_PF_NONE:
|
||||||
|
case PTP_PF_EXTTS:
|
||||||
|
break;
|
||||||
|
case PTP_PF_PEROUT:
|
||||||
|
case PTP_PF_PHYSYNC:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ptp_pin_desc pin_config[MAX_TOD][MAX_REF_CLK];
|
||||||
|
|
||||||
static const struct ptp_clock_info idtcm_caps = {
|
static const struct ptp_clock_info idtcm_caps = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.max_adj = 244000,
|
.max_adj = 244000,
|
||||||
.n_per_out = 12,
|
.n_per_out = 12,
|
||||||
.n_ext_ts = MAX_TOD,
|
.n_ext_ts = MAX_TOD,
|
||||||
|
.n_pins = MAX_REF_CLK,
|
||||||
.adjphase = &idtcm_adjphase,
|
.adjphase = &idtcm_adjphase,
|
||||||
.adjfine = &idtcm_adjfine,
|
.adjfine = &idtcm_adjfine,
|
||||||
.adjtime = &idtcm_adjtime,
|
.adjtime = &idtcm_adjtime,
|
||||||
.gettime64 = &idtcm_gettime,
|
.gettime64 = &idtcm_gettime,
|
||||||
.settime64 = &idtcm_settime,
|
.settime64 = &idtcm_settime,
|
||||||
.enable = &idtcm_enable,
|
.enable = &idtcm_enable,
|
||||||
|
.verify = &idtcm_verify_pin,
|
||||||
.do_aux_work = &idtcm_work_handler,
|
.do_aux_work = &idtcm_work_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2057,12 +2090,14 @@ static const struct ptp_clock_info idtcm_caps_deprecated = {
|
|||||||
.max_adj = 244000,
|
.max_adj = 244000,
|
||||||
.n_per_out = 12,
|
.n_per_out = 12,
|
||||||
.n_ext_ts = MAX_TOD,
|
.n_ext_ts = MAX_TOD,
|
||||||
|
.n_pins = MAX_REF_CLK,
|
||||||
.adjphase = &idtcm_adjphase,
|
.adjphase = &idtcm_adjphase,
|
||||||
.adjfine = &idtcm_adjfine,
|
.adjfine = &idtcm_adjfine,
|
||||||
.adjtime = &idtcm_adjtime_deprecated,
|
.adjtime = &idtcm_adjtime_deprecated,
|
||||||
.gettime64 = &idtcm_gettime,
|
.gettime64 = &idtcm_gettime,
|
||||||
.settime64 = &idtcm_settime_deprecated,
|
.settime64 = &idtcm_settime_deprecated,
|
||||||
.enable = &idtcm_enable,
|
.enable = &idtcm_enable,
|
||||||
|
.verify = &idtcm_verify_pin,
|
||||||
.do_aux_work = &idtcm_work_handler,
|
.do_aux_work = &idtcm_work_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2174,8 +2209,9 @@ static u32 idtcm_get_dco_delay(struct idtcm_channel *channel)
|
|||||||
n = 1;
|
n = 1;
|
||||||
|
|
||||||
fodFreq = (u32)div_u64(m, n);
|
fodFreq = (u32)div_u64(m, n);
|
||||||
|
|
||||||
if (fodFreq >= 500000000)
|
if (fodFreq >= 500000000)
|
||||||
return 18 * (u32)div_u64(NSEC_PER_SEC, fodFreq);
|
return (u32)div_u64(18 * (u64)NSEC_PER_SEC, fodFreq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2188,24 +2224,28 @@ static int configure_channel_tod(struct idtcm_channel *channel, u32 index)
|
|||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0);
|
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0);
|
||||||
|
channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_0);
|
||||||
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0);
|
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0);
|
||||||
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
|
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
|
||||||
channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
|
channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1);
|
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1);
|
||||||
|
channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_1);
|
||||||
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1);
|
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1);
|
||||||
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
|
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
|
||||||
channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
|
channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2);
|
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2);
|
||||||
|
channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_2);
|
||||||
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2);
|
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2);
|
||||||
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
|
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
|
||||||
channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
|
channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3);
|
channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3);
|
||||||
|
channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_3);
|
||||||
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3);
|
channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3);
|
||||||
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
|
channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
|
||||||
channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
|
channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
|
||||||
@ -2221,6 +2261,7 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
|
|||||||
{
|
{
|
||||||
struct idtcm_channel *channel;
|
struct idtcm_channel *channel;
|
||||||
int err;
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!(index < MAX_TOD))
|
if (!(index < MAX_TOD))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -2248,6 +2289,17 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
|
|||||||
snprintf(channel->caps.name, sizeof(channel->caps.name),
|
snprintf(channel->caps.name, sizeof(channel->caps.name),
|
||||||
"IDT CM TOD%u", index);
|
"IDT CM TOD%u", index);
|
||||||
|
|
||||||
|
channel->caps.pin_config = pin_config[index];
|
||||||
|
|
||||||
|
for (i = 0; i < channel->caps.n_pins; ++i) {
|
||||||
|
struct ptp_pin_desc *ppd = &channel->caps.pin_config[i];
|
||||||
|
|
||||||
|
snprintf(ppd->name, sizeof(ppd->name), "input_ref%d", i);
|
||||||
|
ppd->index = i;
|
||||||
|
ppd->func = PTP_PF_NONE;
|
||||||
|
ppd->chan = index;
|
||||||
|
}
|
||||||
|
|
||||||
err = initialize_dco_operating_mode(channel);
|
err = initialize_dco_operating_mode(channel);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@ -2302,26 +2354,40 @@ static int idtcm_enable_extts_channel(struct idtcm *idtcm, u32 index)
|
|||||||
static void idtcm_extts_check(struct work_struct *work)
|
static void idtcm_extts_check(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct idtcm *idtcm = container_of(work, struct idtcm, extts_work.work);
|
struct idtcm *idtcm = container_of(work, struct idtcm, extts_work.work);
|
||||||
int err, i;
|
struct idtcm_channel *channel;
|
||||||
|
u8 mask;
|
||||||
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (idtcm->extts_mask == 0)
|
if (idtcm->extts_mask == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(idtcm->lock);
|
mutex_lock(idtcm->lock);
|
||||||
for (i = 0; i < MAX_TOD; i++) {
|
|
||||||
u8 mask = 1 << i;
|
|
||||||
|
|
||||||
if (idtcm->extts_mask & mask) {
|
for (i = 0; i < MAX_TOD; i++) {
|
||||||
err = idtcm_extts_check_channel(idtcm, i);
|
mask = 1 << i;
|
||||||
|
|
||||||
|
if ((idtcm->extts_mask & mask) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = idtcm_extts_check_channel(idtcm, i);
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
/* trigger clears itself, so clear the mask */
|
/* trigger clears itself, so clear the mask */
|
||||||
if (err == 0)
|
if (idtcm->extts_single_shot) {
|
||||||
idtcm->extts_mask &= ~mask;
|
idtcm->extts_mask &= ~mask;
|
||||||
|
} else {
|
||||||
|
/* Re-arm */
|
||||||
|
channel = &idtcm->channel[i];
|
||||||
|
arm_tod_read_trig_sel_refclk(channel, channel->refn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idtcm->extts_mask)
|
if (idtcm->extts_mask)
|
||||||
schedule_delayed_work(&idtcm->extts_work,
|
schedule_delayed_work(&idtcm->extts_work,
|
||||||
msecs_to_jiffies(EXTTS_PERIOD_MS));
|
msecs_to_jiffies(EXTTS_PERIOD_MS));
|
||||||
|
|
||||||
mutex_unlock(idtcm->lock);
|
mutex_unlock(idtcm->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2342,6 +2408,11 @@ static void set_default_masks(struct idtcm *idtcm)
|
|||||||
idtcm->tod_mask = DEFAULT_TOD_MASK;
|
idtcm->tod_mask = DEFAULT_TOD_MASK;
|
||||||
idtcm->extts_mask = 0;
|
idtcm->extts_mask = 0;
|
||||||
|
|
||||||
|
idtcm->channel[0].tod = 0;
|
||||||
|
idtcm->channel[1].tod = 1;
|
||||||
|
idtcm->channel[2].tod = 2;
|
||||||
|
idtcm->channel[3].tod = 3;
|
||||||
|
|
||||||
idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
|
idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
|
||||||
idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
|
idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
|
||||||
idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
|
idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
|
||||||
@ -2420,8 +2491,8 @@ static int idtcm_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct idtcm *idtcm = platform_get_drvdata(pdev);
|
struct idtcm *idtcm = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
idtcm->extts_mask = 0;
|
||||||
ptp_clock_unregister_all(idtcm);
|
ptp_clock_unregister_all(idtcm);
|
||||||
|
|
||||||
cancel_delayed_work_sync(&idtcm->extts_work);
|
cancel_delayed_work_sync(&idtcm->extts_work);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -10,11 +10,13 @@
|
|||||||
|
|
||||||
#include <linux/ktime.h>
|
#include <linux/ktime.h>
|
||||||
#include <linux/mfd/idt8a340_reg.h>
|
#include <linux/mfd/idt8a340_reg.h>
|
||||||
|
#include <linux/ptp_clock.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#define FW_FILENAME "idtcm.bin"
|
#define FW_FILENAME "idtcm.bin"
|
||||||
#define MAX_TOD (4)
|
#define MAX_TOD (4)
|
||||||
#define MAX_PLL (8)
|
#define MAX_PLL (8)
|
||||||
|
#define MAX_REF_CLK (16)
|
||||||
|
|
||||||
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
|
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
|
||||||
|
|
||||||
@ -90,6 +92,7 @@ struct idtcm_channel {
|
|||||||
u16 dpll_ctrl_n;
|
u16 dpll_ctrl_n;
|
||||||
u16 dpll_phase_pull_in;
|
u16 dpll_phase_pull_in;
|
||||||
u16 tod_read_primary;
|
u16 tod_read_primary;
|
||||||
|
u16 tod_read_secondary;
|
||||||
u16 tod_write;
|
u16 tod_write;
|
||||||
u16 tod_n;
|
u16 tod_n;
|
||||||
u16 hw_dpll_n;
|
u16 hw_dpll_n;
|
||||||
@ -105,6 +108,7 @@ struct idtcm_channel {
|
|||||||
/* last input trigger for extts */
|
/* last input trigger for extts */
|
||||||
u8 refn;
|
u8 refn;
|
||||||
u8 pll;
|
u8 pll;
|
||||||
|
u8 tod;
|
||||||
u16 output_mask;
|
u16 output_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -116,6 +120,7 @@ struct idtcm {
|
|||||||
enum fw_version fw_ver;
|
enum fw_version fw_ver;
|
||||||
/* Polls for external time stamps */
|
/* Polls for external time stamps */
|
||||||
u8 extts_mask;
|
u8 extts_mask;
|
||||||
|
bool extts_single_shot;
|
||||||
struct delayed_work extts_work;
|
struct delayed_work extts_work;
|
||||||
/* Remember the ptp channel to report extts */
|
/* Remember the ptp channel to report extts */
|
||||||
struct idtcm_channel *event_channel[MAX_TOD];
|
struct idtcm_channel *event_channel[MAX_TOD];
|
||||||
|
@ -407,7 +407,7 @@
|
|||||||
#define TOD_READ_PRIMARY_0 0xcc40
|
#define TOD_READ_PRIMARY_0 0xcc40
|
||||||
#define TOD_READ_PRIMARY_0_V520 0xcc50
|
#define TOD_READ_PRIMARY_0_V520 0xcc50
|
||||||
/* 8-bit subns, 32-bit ns, 48-bit seconds */
|
/* 8-bit subns, 32-bit ns, 48-bit seconds */
|
||||||
#define TOD_READ_PRIMARY 0x0000
|
#define TOD_READ_PRIMARY_BASE 0x0000
|
||||||
/* Counter increments after TOD write is completed */
|
/* Counter increments after TOD write is completed */
|
||||||
#define TOD_READ_PRIMARY_COUNTER 0x000b
|
#define TOD_READ_PRIMARY_COUNTER 0x000b
|
||||||
/* Read trigger configuration */
|
/* Read trigger configuration */
|
||||||
@ -424,6 +424,16 @@
|
|||||||
|
|
||||||
#define TOD_READ_SECONDARY_0 0xcc90
|
#define TOD_READ_SECONDARY_0 0xcc90
|
||||||
#define TOD_READ_SECONDARY_0_V520 0xcca0
|
#define TOD_READ_SECONDARY_0_V520 0xcca0
|
||||||
|
/* 8-bit subns, 32-bit ns, 48-bit seconds */
|
||||||
|
#define TOD_READ_SECONDARY_BASE 0x0000
|
||||||
|
/* Counter increments after TOD write is completed */
|
||||||
|
#define TOD_READ_SECONDARY_COUNTER 0x000b
|
||||||
|
/* Read trigger configuration */
|
||||||
|
#define TOD_READ_SECONDARY_SEL_CFG_0 0x000c
|
||||||
|
/* Read trigger selection */
|
||||||
|
#define TOD_READ_SECONDARY_CMD 0x000e
|
||||||
|
#define TOD_READ_SECONDARY_CMD_V520 0x000f
|
||||||
|
|
||||||
#define TOD_READ_SECONDARY_1 0xcca0
|
#define TOD_READ_SECONDARY_1 0xcca0
|
||||||
#define TOD_READ_SECONDARY_1_V520 0xccb0
|
#define TOD_READ_SECONDARY_1_V520 0xccb0
|
||||||
#define TOD_READ_SECONDARY_2 0xccb0
|
#define TOD_READ_SECONDARY_2 0xccb0
|
||||||
|
Loading…
Reference in New Issue
Block a user