summaryrefslogtreecommitdiffstats
path: root/drivers/ptp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ptp')
-rw-r--r--drivers/ptp/ptp_clock.c31
-rw-r--r--drivers/ptp/ptp_clockmatrix.c321
-rw-r--r--drivers/ptp/ptp_clockmatrix.h7
-rw-r--r--drivers/ptp/ptp_ocp.c559
-rw-r--r--drivers/ptp/ptp_private.h11
-rw-r--r--drivers/ptp/ptp_sysfs.c11
-rw-r--r--drivers/ptp/ptp_vclock.c82
7 files changed, 633 insertions, 389 deletions
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index b6f2cfd15dd2..688cde320bb0 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -77,8 +77,8 @@ static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
- if (ptp_vclock_in_use(ptp)) {
- pr_err("ptp: virtual clock in use\n");
+ if (ptp_clock_freerun(ptp)) {
+ pr_err("ptp: physical clock is free running\n");
return -EBUSY;
}
@@ -103,8 +103,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
struct ptp_clock_info *ops;
int err = -EOPNOTSUPP;
- if (ptp_vclock_in_use(ptp)) {
- pr_err("ptp: virtual clock in use\n");
+ if (ptp_clock_freerun(ptp)) {
+ pr_err("ptp: physical clock is free running\n");
return -EBUSY;
}
@@ -178,6 +178,14 @@ static void ptp_clock_release(struct device *dev)
kfree(ptp);
}
+static int ptp_getcycles64(struct ptp_clock_info *info, struct timespec64 *ts)
+{
+ if (info->getcyclesx64)
+ return info->getcyclesx64(info, ts, NULL);
+ else
+ return info->gettime64(info, ts);
+}
+
static void ptp_aux_kworker(struct kthread_work *work)
{
struct ptp_clock *ptp = container_of(work, struct ptp_clock,
@@ -225,6 +233,21 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
mutex_init(&ptp->n_vclocks_mux);
init_waitqueue_head(&ptp->tsev_wq);
+ if (ptp->info->getcycles64 || ptp->info->getcyclesx64) {
+ ptp->has_cycles = true;
+ if (!ptp->info->getcycles64 && ptp->info->getcyclesx64)
+ ptp->info->getcycles64 = ptp_getcycles64;
+ } else {
+ /* Free running cycle counter not supported, use time. */
+ ptp->info->getcycles64 = ptp_getcycles64;
+
+ if (ptp->info->gettimex64)
+ ptp->info->getcyclesx64 = ptp->info->gettimex64;
+
+ if (ptp->info->getcrosststamp)
+ ptp->info->getcrosscycles = ptp->info->getcrosststamp;
+ }
+
if (ptp->info->do_aux_work) {
kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker);
ptp->kworker = kthread_create_worker(0, "ptp%d", ptp->index);
diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index 08e429a06922..cb258e1448d5 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -239,73 +239,97 @@ static int wait_for_boot_status_ready(struct idtcm *idtcm)
return -EBUSY;
}
-static int _idtcm_set_scsr_read_trig(struct idtcm_channel *channel,
- enum scsr_read_trig_sel trig, u8 ref)
+static int arm_tod_read_trig_sel_refclk(struct idtcm_channel *channel, u8 ref)
{
struct idtcm *idtcm = channel->idtcm;
- u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
- u8 val;
+ u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
+ u8 val = 0;
int err;
- if (trig == SCSR_TOD_READ_TRIG_SEL_REFCLK) {
- err = idtcm_read(idtcm, channel->tod_read_primary,
- TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
- if (err)
- return err;
-
- val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
- val |= (ref << WR_REF_INDEX_SHIFT);
-
- err = idtcm_write(idtcm, channel->tod_read_primary,
- TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
- if (err)
- return err;
- }
+ val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
+ val |= (ref << WR_REF_INDEX_SHIFT);
- err = idtcm_read(idtcm, channel->tod_read_primary,
- tod_read_cmd, &val, sizeof(val));
+ err = idtcm_write(idtcm, channel->tod_read_secondary,
+ TOD_READ_SECONDARY_SEL_CFG_0, &val, sizeof(val));
if (err)
return err;
- val &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
- val |= (trig << TOD_READ_TRIGGER_SHIFT);
- val &= ~TOD_READ_TRIGGER_MODE; /* single shot */
+ val = 0 | (SCSR_TOD_READ_TRIG_SEL_REFCLK << TOD_READ_TRIGGER_SHIFT);
+
+ 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;
}
-static int idtcm_enable_extts(struct idtcm_channel *channel, u8 todn, u8 ref,
- bool enable)
+static bool is_single_shot(u8 mask)
{
- struct idtcm *idtcm = channel->idtcm;
- u8 old_mask = idtcm->extts_mask;
- u8 mask = 1 << todn;
+ /* Treat single bit ToD masks as continuous trigger */
+ return mask <= 8 && is_power_of_2(mask);
+}
+
+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;
- if (todn >= MAX_TOD)
+ 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;
- if (enable) {
- if (ref > 0xF) /* E_REF_CLK15 */
- return -EINVAL;
- if (idtcm->extts_mask & mask)
- return 0;
- err = _idtcm_set_scsr_read_trig(&idtcm->channel[todn],
- SCSR_TOD_READ_TRIG_SEL_REFCLK,
- ref);
+ if (on) {
+ /* Support triggering more than one TOD_0/1/2/3 by same pin */
+ /* Use the pin configured for the channel */
+ ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->tod);
+
+ if (ref < 0) {
+ dev_err(idtcm->dev, "%s: No valid pin found for TOD%d!\n",
+ __func__, channel->tod);
+ return -EBUSY;
+ }
+
+ err = arm_tod_read_trig_sel_refclk(&idtcm->channel[index], ref);
+
if (err == 0) {
idtcm->extts_mask |= mask;
- idtcm->event_channel[todn] = channel;
- idtcm->channel[todn].refn = ref;
+ idtcm->event_channel[index] = channel;
+ idtcm->channel[index].refn = ref;
+ idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
+
+ if (old_mask)
+ return 0;
+
+ schedule_delayed_work(&idtcm->extts_work,
+ msecs_to_jiffies(EXTTS_PERIOD_MS));
}
- } else
+ } else {
idtcm->extts_mask &= ~mask;
+ idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
- if (old_mask == 0 && idtcm->extts_mask)
- schedule_delayed_work(&idtcm->extts_work,
- msecs_to_jiffies(EXTTS_PERIOD_MS));
+ if (idtcm->extts_mask == 0)
+ cancel_delayed_work(&idtcm->extts_work);
+ }
return err;
}
@@ -371,6 +395,31 @@ static void wait_for_chip_ready(struct idtcm *idtcm)
"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,
struct timespec64 *ts, u8 timeout)
{
@@ -396,7 +445,7 @@ static int _idtcm_gettime(struct idtcm_channel *channel,
} while (trigger & TOD_READ_TRIGGER_MASK);
err = idtcm_read(idtcm, channel->tod_read_primary,
- TOD_READ_PRIMARY, buf, sizeof(buf));
+ TOD_READ_PRIMARY_BASE, buf, sizeof(buf));
if (err)
return err;
@@ -415,67 +464,38 @@ static int idtcm_extts_check_channel(struct idtcm *idtcm, u8 todn)
extts_channel = &idtcm->channel[todn];
ptp_channel = idtcm->event_channel[todn];
+
if (extts_channel == ptp_channel)
dco_delay = ptp_channel->dco_delay;
- err = _idtcm_gettime(extts_channel, &ts, 1);
- if (err == 0) {
- 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;
-}
+ err = _idtcm_gettime_triggered(extts_channel, &ts);
+ if (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;
+ /* Triggered - save timestamp */
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = todn;
+ event.timestamp = timespec64_to_ns(&ts) - dco_delay;
+ ptp_clock_event(ptp_channel->ptp_clock, &event);
- 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;
+ return err;
}
static int _idtcm_gettime_immediate(struct idtcm_channel *channel,
struct timespec64 *ts)
{
struct idtcm *idtcm = channel->idtcm;
- u8 extts_mask = 0;
- int err;
- /* Disable extts */
- if (idtcm->extts_mask) {
- extts_mask = idtcm_enable_extts_mask(channel, idtcm->extts_mask,
- false);
- }
-
- err = _idtcm_set_scsr_read_trig(channel,
- SCSR_TOD_READ_TRIG_SEL_IMMEDIATE, 0);
- if (err == 0)
- err = _idtcm_gettime(channel, ts, 10);
+ 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;
- /* Re-enable extts */
- if (extts_mask)
- idtcm_enable_extts_mask(channel, extts_mask, true);
+ err = idtcm_write(idtcm, channel->tod_read_primary,
+ tod_read_cmd, &val, sizeof(val));
+ if (err)
+ return err;
- return err;
+ return _idtcm_gettime(channel, ts, 10);
}
static int _sync_pll_output(struct idtcm *idtcm,
@@ -1332,43 +1352,15 @@ static int idtcm_output_enable(struct idtcm_channel *channel,
return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
}
-static int idtcm_output_mask_enable(struct idtcm_channel *channel,
- bool enable)
-{
- u16 mask;
- int err;
- u8 outn;
-
- mask = channel->output_mask;
- outn = 0;
-
- while (mask) {
- if (mask & 0x1) {
- err = idtcm_output_enable(channel, enable, outn);
- if (err)
- return err;
- }
-
- mask >>= 0x1;
- outn++;
- }
-
- return 0;
-}
-
static int idtcm_perout_enable(struct idtcm_channel *channel,
struct ptp_perout_request *perout,
bool enable)
{
struct idtcm *idtcm = channel->idtcm;
- unsigned int flags = perout->flags;
struct timespec64 ts = {0, 0};
int err;
- if (flags == PEROUT_ENABLE_OUTPUT_MASK)
- err = idtcm_output_mask_enable(channel, enable);
- else
- err = idtcm_output_enable(channel, enable, perout->index);
+ err = idtcm_output_enable(channel, enable, perout->index);
if (err) {
dev_err(idtcm->dev, "Unable to set output enable");
@@ -1702,6 +1694,9 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel)
/*
* 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
*
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
@@ -1869,7 +1864,7 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
int err;
if (channel->phase_pull_in == true)
- return 0;
+ return -EBUSY;
mutex_lock(idtcm->lock);
@@ -1958,8 +1953,7 @@ static int idtcm_enable(struct ptp_clock_info *ptp,
err = idtcm_perout_enable(channel, &rq->perout, true);
break;
case PTP_CLK_REQ_EXTTS:
- err = idtcm_enable_extts(channel, rq->extts.index,
- rq->extts.rsv[0], on);
+ err = idtcm_extts_enable(channel, rq, on);
break;
default:
break;
@@ -1982,13 +1976,6 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
u8 cfg;
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.
*/
@@ -2038,17 +2025,35 @@ static void idtcm_set_version_info(struct idtcm *idtcm)
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 = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 12,
.n_ext_ts = MAX_TOD,
+ .n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime,
.settime64 = &idtcm_settime,
.enable = &idtcm_enable,
+ .verify = &idtcm_verify_pin,
.do_aux_work = &idtcm_work_handler,
};
@@ -2057,12 +2062,14 @@ static const struct ptp_clock_info idtcm_caps_deprecated = {
.max_adj = 244000,
.n_per_out = 12,
.n_ext_ts = MAX_TOD,
+ .n_pins = MAX_REF_CLK,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
.adjtime = &idtcm_adjtime_deprecated,
.gettime64 = &idtcm_gettime,
.settime64 = &idtcm_settime_deprecated,
.enable = &idtcm_enable,
+ .verify = &idtcm_verify_pin,
.do_aux_work = &idtcm_work_handler,
};
@@ -2174,8 +2181,9 @@ static u32 idtcm_get_dco_delay(struct idtcm_channel *channel)
n = 1;
fodFreq = (u32)div_u64(m, n);
+
if (fodFreq >= 500000000)
- return 18 * (u32)div_u64(NSEC_PER_SEC, fodFreq);
+ return (u32)div_u64(18 * (u64)NSEC_PER_SEC, fodFreq);
return 0;
}
@@ -2188,24 +2196,28 @@ static int configure_channel_tod(struct idtcm_channel *channel, u32 index)
switch (index) {
case 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_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
break;
case 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_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
break;
case 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_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
break;
case 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_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
@@ -2221,6 +2233,7 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
{
struct idtcm_channel *channel;
int err;
+ int i;
if (!(index < MAX_TOD))
return -EINVAL;
@@ -2248,6 +2261,17 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
snprintf(channel->caps.name, sizeof(channel->caps.name),
"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);
if (err)
return err;
@@ -2302,26 +2326,40 @@ static int idtcm_enable_extts_channel(struct idtcm *idtcm, u32 index)
static void idtcm_extts_check(struct work_struct *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)
return;
mutex_lock(idtcm->lock);
+
for (i = 0; i < MAX_TOD; i++) {
- u8 mask = 1 << i;
+ mask = 1 << i;
+
+ if ((idtcm->extts_mask & mask) == 0)
+ continue;
- if (idtcm->extts_mask & mask) {
- err = idtcm_extts_check_channel(idtcm, i);
+ err = idtcm_extts_check_channel(idtcm, i);
+
+ if (err == 0) {
/* trigger clears itself, so clear the mask */
- if (err == 0)
+ if (idtcm->extts_single_shot) {
idtcm->extts_mask &= ~mask;
+ } else {
+ /* Re-arm */
+ channel = &idtcm->channel[i];
+ arm_tod_read_trig_sel_refclk(channel, channel->refn);
+ }
}
}
if (idtcm->extts_mask)
schedule_delayed_work(&idtcm->extts_work,
msecs_to_jiffies(EXTTS_PERIOD_MS));
+
mutex_unlock(idtcm->lock);
}
@@ -2342,6 +2380,11 @@ static void set_default_masks(struct idtcm *idtcm)
idtcm->tod_mask = DEFAULT_TOD_MASK;
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[1].pll = DEFAULT_TOD1_PTP_PLL;
idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
@@ -2420,8 +2463,8 @@ static int idtcm_remove(struct platform_device *pdev)
{
struct idtcm *idtcm = platform_get_drvdata(pdev);
+ idtcm->extts_mask = 0;
ptp_clock_unregister_all(idtcm);
-
cancel_delayed_work_sync(&idtcm->extts_work);
return 0;
diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 0f3059ae1fff..bf1e49409844 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -10,11 +10,13 @@
#include <linux/ktime.h>
#include <linux/mfd/idt8a340_reg.h>
+#include <linux/ptp_clock.h>
#include <linux/regmap.h>
#define FW_FILENAME "idtcm.bin"
#define MAX_TOD (4)
#define MAX_PLL (8)
+#define MAX_REF_CLK (16)
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
@@ -52,8 +54,6 @@
#define LOCK_TIMEOUT_MS (2000)
#define LOCK_POLL_INTERVAL_MS (10)
-#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
-
#define IDTCM_MAX_WRITE_COUNT (512)
#define PHASE_PULL_IN_MAX_PPB (144000)
@@ -90,6 +90,7 @@ struct idtcm_channel {
u16 dpll_ctrl_n;
u16 dpll_phase_pull_in;
u16 tod_read_primary;
+ u16 tod_read_secondary;
u16 tod_write;
u16 tod_n;
u16 hw_dpll_n;
@@ -105,6 +106,7 @@ struct idtcm_channel {
/* last input trigger for extts */
u8 refn;
u8 pll;
+ u8 tod;
u16 output_mask;
};
@@ -116,6 +118,7 @@ struct idtcm {
enum fw_version fw_ver;
/* Polls for external time stamps */
u8 extts_mask;
+ bool extts_single_shot;
struct delayed_work extts_work;
/* Remember the ptp channel to report extts */
struct idtcm_channel *event_channel[MAX_TOD];
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 860672d6a03c..4519ef42b458 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -19,14 +19,13 @@
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
#include <linux/nvmem-consumer.h>
+#include <linux/crc16.h>
-#ifndef PCI_VENDOR_ID_FACEBOOK
-#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
-#endif
+#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
+#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
-#ifndef PCI_DEVICE_ID_FACEBOOK_TIMECARD
-#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
-#endif
+#define PCI_VENDOR_ID_CELESTICA 0x18d4
+#define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008
static struct class timecard_class = {
.owner = THIS_MODULE,
@@ -215,6 +214,17 @@ struct ptp_ocp_flash_info {
void *data;
};
+struct ptp_ocp_firmware_header {
+ char magic[4];
+ __be16 pci_vendor_id;
+ __be16 pci_device_id;
+ __be32 image_size;
+ __be16 hw_revision;
+ __be16 crc;
+};
+
+#define OCP_FIRMWARE_MAGIC_HEADER "OCPC"
+
struct ptp_ocp_i2c_info {
const char *name;
unsigned long fixed_rate;
@@ -245,6 +255,7 @@ struct ptp_ocp_sma_connector {
bool fixed_fcn;
bool fixed_dir;
bool disabled;
+ u8 default_fcn;
};
struct ocp_attr_group {
@@ -310,7 +321,9 @@ struct ptp_ocp {
int gnss2_port;
int mac_port; /* miniature atomic clock */
int nmea_port;
- u32 fw_version;
+ bool fw_loader;
+ u8 fw_tag;
+ u16 fw_version;
u8 board_id[OCP_BOARD_ID_LEN];
u8 serial[OCP_SERIAL_LEN];
bool has_eeprom_data;
@@ -321,6 +334,7 @@ struct ptp_ocp {
u64 fw_cap;
struct ptp_ocp_signal signal[4];
struct ptp_ocp_sma_connector sma[4];
+ const struct ocp_sma_op *sma_op;
};
#define OCP_REQ_TIMESTAMP BIT(0)
@@ -634,7 +648,8 @@ static struct ocp_resource ocp_fb_resource[] = {
static const struct pci_device_id ptp_ocp_pcidev_id[] = {
{ PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
- { 0 }
+ { PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
+ { }
};
MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
@@ -646,7 +661,7 @@ struct ocp_selector {
int value;
};
-static struct ocp_selector ptp_ocp_clock[] = {
+static const struct ocp_selector ptp_ocp_clock[] = {
{ .name = "NONE", .value = 0 },
{ .name = "TOD", .value = 1 },
{ .name = "IRIG", .value = 2 },
@@ -663,7 +678,7 @@ static struct ocp_selector ptp_ocp_clock[] = {
#define SMA_SELECT_MASK ((1U << 15) - 1)
#define SMA_DISABLE 0x10000
-static struct ocp_selector ptp_ocp_sma_in[] = {
+static const struct ocp_selector ptp_ocp_sma_in[] = {
{ .name = "10Mhz", .value = 0x0000 },
{ .name = "PPS1", .value = 0x0001 },
{ .name = "PPS2", .value = 0x0002 },
@@ -681,7 +696,7 @@ static struct ocp_selector ptp_ocp_sma_in[] = {
{ }
};
-static struct ocp_selector ptp_ocp_sma_out[] = {
+static const struct ocp_selector ptp_ocp_sma_out[] = {
{ .name = "10Mhz", .value = 0x0000 },
{ .name = "PHC", .value = 0x0001 },
{ .name = "MAC", .value = 0x0002 },
@@ -698,8 +713,40 @@ static struct ocp_selector ptp_ocp_sma_out[] = {
{ }
};
+struct ocp_sma_op {
+ const struct ocp_selector *tbl[2];
+ void (*init)(struct ptp_ocp *bp);
+ u32 (*get)(struct ptp_ocp *bp, int sma_nr);
+ int (*set_inputs)(struct ptp_ocp *bp, int sma_nr, u32 val);
+ int (*set_output)(struct ptp_ocp *bp, int sma_nr, u32 val);
+};
+
+static void
+ptp_ocp_sma_init(struct ptp_ocp *bp)
+{
+ return bp->sma_op->init(bp);
+}
+
+static u32
+ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr)
+{
+ return bp->sma_op->get(bp, sma_nr);
+}
+
+static int
+ptp_ocp_sma_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ return bp->sma_op->set_inputs(bp, sma_nr, val);
+}
+
+static int
+ptp_ocp_sma_set_output(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ return bp->sma_op->set_output(bp, sma_nr, val);
+}
+
static const char *
-ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
+ptp_ocp_select_name_from_val(const struct ocp_selector *tbl, int val)
{
int i;
@@ -710,7 +757,7 @@ ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
}
static int
-ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
+ptp_ocp_select_val_from_name(const struct ocp_selector *tbl, const char *name)
{
const char *select;
int i;
@@ -724,7 +771,7 @@ ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
}
static ssize_t
-ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf)
+ptp_ocp_select_table_show(const struct ocp_selector *tbl, char *buf)
{
ssize_t count;
int i;
@@ -1289,24 +1336,80 @@ ptp_ocp_find_flash(struct ptp_ocp *bp)
}
static int
+ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
+ const u8 **data, size_t *size)
+{
+ struct ptp_ocp *bp = devlink_priv(devlink);
+ const struct ptp_ocp_firmware_header *hdr;
+ size_t offset, length;
+ u16 crc;
+
+ hdr = (const struct ptp_ocp_firmware_header *)fw->data;
+ if (memcmp(hdr->magic, OCP_FIRMWARE_MAGIC_HEADER, 4)) {
+ devlink_flash_update_status_notify(devlink,
+ "No firmware header found, flashing raw image",
+ NULL, 0, 0);
+ offset = 0;
+ length = fw->size;
+ goto out;
+ }
+
+ if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor ||
+ be16_to_cpu(hdr->pci_device_id) != bp->pdev->device) {
+ devlink_flash_update_status_notify(devlink,
+ "Firmware image compatibility check failed",
+ NULL, 0, 0);
+ return -EINVAL;
+ }
+
+ offset = sizeof(*hdr);
+ length = be32_to_cpu(hdr->image_size);
+ if (length != (fw->size - offset)) {
+ devlink_flash_update_status_notify(devlink,
+ "Firmware image size check failed",
+ NULL, 0, 0);
+ return -EINVAL;
+ }
+
+ crc = crc16(0xffff, &fw->data[offset], length);
+ if (be16_to_cpu(hdr->crc) != crc) {
+ devlink_flash_update_status_notify(devlink,
+ "Firmware image CRC check failed",
+ NULL, 0, 0);
+ return -EINVAL;
+ }
+
+out:
+ *data = &fw->data[offset];
+ *size = length;
+
+ return 0;
+}
+
+static int
ptp_ocp_devlink_flash(struct devlink *devlink, struct device *dev,
const struct firmware *fw)
{
struct mtd_info *mtd = dev_get_drvdata(dev);
struct ptp_ocp *bp = devlink_priv(devlink);
- size_t off, len, resid, wrote;
+ size_t off, len, size, resid, wrote;
struct erase_info erase;
size_t base, blksz;
- int err = 0;
+ const u8 *data;
+ int err;
+
+ err = ptp_ocp_devlink_fw_image(devlink, fw, &data, &size);
+ if (err)
+ goto out;
off = 0;
base = bp->flash_start;
blksz = 4096;
- resid = fw->size;
+ resid = size;
while (resid) {
devlink_flash_update_status_notify(devlink, "Flashing",
- NULL, off, fw->size);
+ NULL, off, size);
len = min_t(size_t, resid, blksz);
erase.addr = base + off;
@@ -1316,7 +1419,7 @@ ptp_ocp_devlink_flash(struct devlink *devlink, struct device *dev,
if (err)
goto out;
- err = mtd_write(mtd, base + off, len, &wrote, &fw->data[off]);
+ err = mtd_write(mtd, base + off, len, &wrote, data + off);
if (err)
goto out;
@@ -1360,6 +1463,7 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct ptp_ocp *bp = devlink_priv(devlink);
+ const char *fw_image;
char buf[32];
int err;
@@ -1367,13 +1471,9 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
if (err)
return err;
- if (bp->fw_version & 0xffff) {
- sprintf(buf, "%d", bp->fw_version);
- err = devlink_info_version_running_put(req, "fw", buf);
- } else {
- sprintf(buf, "%d", bp->fw_version >> 16);
- err = devlink_info_version_running_put(req, "loader", buf);
- }
+ fw_image = bp->fw_loader ? "loader" : "fw";
+ sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version);
+ err = devlink_info_version_running_put(req, fw_image, buf);
if (err)
return err;
@@ -1403,7 +1503,7 @@ static const struct devlink_ops ptp_ocp_devlink_ops = {
};
static void __iomem *
-__ptp_ocp_get_mem(struct ptp_ocp *bp, unsigned long start, int size)
+__ptp_ocp_get_mem(struct ptp_ocp *bp, resource_size_t start, int size)
{
struct resource res = DEFINE_RES_MEM_NAMED(start, size, "ptp_ocp");
@@ -1413,7 +1513,7 @@ __ptp_ocp_get_mem(struct ptp_ocp *bp, unsigned long start, int size)
static void __iomem *
ptp_ocp_get_mem(struct ptp_ocp *bp, struct ocp_resource *r)
{
- unsigned long start;
+ resource_size_t start;
start = pci_resource_start(bp->pdev, 0) + r->offset;
return __ptp_ocp_get_mem(bp, start, r->size);
@@ -1427,7 +1527,7 @@ ptp_ocp_set_irq_resource(struct resource *res, int irq)
}
static void
-ptp_ocp_set_mem_resource(struct resource *res, unsigned long start, int size)
+ptp_ocp_set_mem_resource(struct resource *res, resource_size_t start, int size)
{
struct resource r = DEFINE_RES_MEM(start, size);
*res = r;
@@ -1440,7 +1540,7 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r)
struct pci_dev *pdev = bp->pdev;
struct platform_device *p;
struct resource res[2];
- unsigned long start;
+ resource_size_t start;
int id;
start = pci_resource_start(pdev, 0) + r->offset;
@@ -1467,7 +1567,7 @@ ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id)
{
struct ptp_ocp_i2c_info *info;
struct resource res[2];
- unsigned long start;
+ resource_size_t start;
info = r->extra;
start = pci_resource_start(pdev, 0) + r->offset;
@@ -1873,7 +1973,134 @@ ptp_ocp_attr_group_add(struct ptp_ocp *bp,
}
static void
-ptp_ocp_sma_init(struct ptp_ocp *bp)
+ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable)
+{
+ u32 ctrl;
+ bool on;
+
+ ctrl = ioread32(reg);
+ on = ctrl & bit;
+ if (on ^ enable) {
+ ctrl &= ~bit;
+ ctrl |= enable ? bit : 0;
+ iowrite32(ctrl, reg);
+ }
+}
+
+static void
+ptp_ocp_irig_out(struct ptp_ocp *bp, bool enable)
+{
+ return ptp_ocp_enable_fpga(&bp->irig_out->ctrl,
+ IRIG_M_CTRL_ENABLE, enable);
+}
+
+static void
+ptp_ocp_irig_in(struct ptp_ocp *bp, bool enable)
+{
+ return ptp_ocp_enable_fpga(&bp->irig_in->ctrl,
+ IRIG_S_CTRL_ENABLE, enable);
+}
+
+static void
+ptp_ocp_dcf_out(struct ptp_ocp *bp, bool enable)
+{
+ return ptp_ocp_enable_fpga(&bp->dcf_out->ctrl,
+ DCF_M_CTRL_ENABLE, enable);
+}
+
+static void
+ptp_ocp_dcf_in(struct ptp_ocp *bp, bool enable)
+{
+ return ptp_ocp_enable_fpga(&bp->dcf_in->ctrl,
+ DCF_S_CTRL_ENABLE, enable);
+}
+
+static void
+__handle_signal_outputs(struct ptp_ocp *bp, u32 val)
+{
+ ptp_ocp_irig_out(bp, val & 0x00100010);
+ ptp_ocp_dcf_out(bp, val & 0x00200020);
+}
+
+static void
+__handle_signal_inputs(struct ptp_ocp *bp, u32 val)
+{
+ ptp_ocp_irig_in(bp, val & 0x00100010);
+ ptp_ocp_dcf_in(bp, val & 0x00200020);
+}
+
+static u32
+ptp_ocp_sma_fb_get(struct ptp_ocp *bp, int sma_nr)
+{
+ u32 __iomem *gpio;
+ u32 shift;
+
+ if (bp->sma[sma_nr - 1].fixed_fcn)
+ return (sma_nr - 1) & 1;
+
+ if (bp->sma[sma_nr - 1].mode == SMA_MODE_IN)
+ gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
+ else
+ gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
+ shift = sma_nr & 1 ? 0 : 16;
+
+ return (ioread32(gpio) >> shift) & 0xffff;
+}
+
+static int
+ptp_ocp_sma_fb_set_output(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ u32 reg, mask, shift;
+ unsigned long flags;
+ u32 __iomem *gpio;
+
+ gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
+ shift = sma_nr & 1 ? 0 : 16;
+
+ mask = 0xffff << (16 - shift);
+
+ spin_lock_irqsave(&bp->lock, flags);
+
+ reg = ioread32(gpio);
+ reg = (reg & mask) | (val << shift);
+
+ __handle_signal_outputs(bp, reg);
+
+ iowrite32(reg, gpio);
+
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
+static int
+ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ u32 reg, mask, shift;
+ unsigned long flags;
+ u32 __iomem *gpio;
+
+ gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
+ shift = sma_nr & 1 ? 0 : 16;
+
+ mask = 0xffff << (16 - shift);
+
+ spin_lock_irqsave(&bp->lock, flags);
+
+ reg = ioread32(gpio);
+ reg = (reg & mask) | (val << shift);
+
+ __handle_signal_inputs(bp, reg);
+
+ iowrite32(reg, gpio);
+
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
+static void
+ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
{
u32 reg;
int i;
@@ -1883,6 +2110,8 @@ ptp_ocp_sma_init(struct ptp_ocp *bp)
bp->sma[1].mode = SMA_MODE_IN;
bp->sma[2].mode = SMA_MODE_OUT;
bp->sma[3].mode = SMA_MODE_OUT;
+ for (i = 0; i < 4; i++)
+ bp->sma[i].default_fcn = i & 1;
/* If no SMA1 map, the pin functions and directions are fixed. */
if (!bp->sma_map1) {
@@ -1911,6 +2140,14 @@ ptp_ocp_sma_init(struct ptp_ocp *bp)
}
}
+static const struct ocp_sma_op ocp_fb_sma_op = {
+ .tbl = { ptp_ocp_sma_in, ptp_ocp_sma_out },
+ .init = ptp_ocp_sma_fb_init,
+ .get = ptp_ocp_sma_fb_get,
+ .set_inputs = ptp_ocp_sma_fb_set_inputs,
+ .set_output = ptp_ocp_sma_fb_set_output,
+};
+
static int
ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
{
@@ -1932,22 +2169,50 @@ ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
return 0;
}
+static void
+ptp_ocp_fb_set_version(struct ptp_ocp *bp)
+{
+ u64 cap = OCP_CAP_BASIC;
+ u32 version;
+
+ version = ioread32(&bp->image->version);
+
+ /* if lower 16 bits are empty, this is the fw loader. */
+ if ((version & 0xffff) == 0) {
+ version = version >> 16;
+ bp->fw_loader = true;
+ }
+
+ bp->fw_tag = version >> 15;
+ bp->fw_version = version & 0x7fff;
+
+ if (bp->fw_tag) {
+ /* FPGA firmware */
+ if (version >= 5)
+ cap |= OCP_CAP_SIGNAL | OCP_CAP_FREQ;
+ } else {
+ /* SOM firmware */
+ if (version >= 19)
+ cap |= OCP_CAP_SIGNAL;
+ if (version >= 20)
+ cap |= OCP_CAP_FREQ;
+ }
+
+ bp->fw_cap = cap;
+}
+
/* FB specific board initializers; last "resource" registered. */
static int
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
{
- int ver, err;
+ int err;
bp->flash_start = 1024 * 4096;
bp->eeprom_map = fb_eeprom_map;
bp->fw_version = ioread32(&bp->image->version);
- bp->fw_cap = OCP_CAP_BASIC;
+ bp->sma_op = &ocp_fb_sma_op;
- ver = bp->fw_version & 0xffff;
- if (ver >= 19)
- bp->fw_cap |= OCP_CAP_SIGNAL;
- if (ver >= 20)
- bp->fw_cap |= OCP_CAP_FREQ;
+ ptp_ocp_fb_set_version(bp);
ptp_ocp_tod_init(bp);
ptp_ocp_nmea_out_init(bp);
@@ -1997,101 +2262,38 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
return err;
}
-static void
-ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable)
-{
- u32 ctrl;
- bool on;
-
- ctrl = ioread32(reg);
- on = ctrl & bit;
- if (on ^ enable) {
- ctrl &= ~bit;
- ctrl |= enable ? bit : 0;
- iowrite32(ctrl, reg);
- }
-}
-
-static void
-ptp_ocp_irig_out(struct ptp_ocp *bp, bool enable)
-{
- return ptp_ocp_enable_fpga(&bp->irig_out->ctrl,
- IRIG_M_CTRL_ENABLE, enable);
-}
-
-static void
-ptp_ocp_irig_in(struct ptp_ocp *bp, bool enable)
-{
- return ptp_ocp_enable_fpga(&bp->irig_in->ctrl,
- IRIG_S_CTRL_ENABLE, enable);
-}
-
-static void
-ptp_ocp_dcf_out(struct ptp_ocp *bp, bool enable)
-{
- return ptp_ocp_enable_fpga(&bp->dcf_out->ctrl,
- DCF_M_CTRL_ENABLE, enable);
-}
-
-static void
-ptp_ocp_dcf_in(struct ptp_ocp *bp, bool enable)
-{
- return ptp_ocp_enable_fpga(&bp->dcf_in->ctrl,
- DCF_S_CTRL_ENABLE, enable);
-}
-
-static void
-__handle_signal_outputs(struct ptp_ocp *bp, u32 val)
-{
- ptp_ocp_irig_out(bp, val & 0x00100010);
- ptp_ocp_dcf_out(bp, val & 0x00200020);
-}
-
-static void
-__handle_signal_inputs(struct ptp_ocp *bp, u32 val)
-{
- ptp_ocp_irig_in(bp, val & 0x00100010);
- ptp_ocp_dcf_in(bp, val & 0x00200020);
-}
-
-/*
- * ANT0 == gps (in)
- * ANT1 == sma1 (in)
- * ANT2 == sma2 (in)
- * ANT3 == sma3 (out)
- * ANT4 == sma4 (out)
- */
-
static ssize_t
-ptp_ocp_show_output(u32 val, char *buf, int def_val)
+ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf,
+ int def_val)
{
const char *name;
ssize_t count;
count = sysfs_emit(buf, "OUT: ");
- name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val);
+ name = ptp_ocp_select_name_from_val(tbl, val);
if (!name)
- name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, def_val);
+ name = ptp_ocp_select_name_from_val(tbl, def_val);
count += sysfs_emit_at(buf, count, "%s\n", name);
return count;
}
static ssize_t
-ptp_ocp_show_inputs(u32 val, char *buf, int def_val)
+ptp_ocp_show_inputs(const struct ocp_selector *tbl, u32 val, char *buf,
+ int def_val)
{
const char *name;
ssize_t count;
int i;
count = sysfs_emit(buf, "IN: ");
- for (i = 0; i < ARRAY_SIZE(ptp_ocp_sma_in); i++) {
- if (val & ptp_ocp_sma_in[i].value) {
- name = ptp_ocp_sma_in[i].name;
+ for (i = 0; tbl[i].name; i++) {
+ if (val & tbl[i].value) {
+ name = tbl[i].name;
count += sysfs_emit_at(buf, count, "%s ", name);
}
}
if (!val && def_val >= 0) {
- name = ptp_ocp_select_name_from_val(ptp_ocp_sma_in, def_val);
+ name = ptp_ocp_select_name_from_val(tbl, def_val);
count += sysfs_emit_at(buf, count, "%s ", name);
}
if (count)
@@ -2101,9 +2303,9 @@ ptp_ocp_show_inputs(u32 val, char *buf, int def_val)
}
static int
-sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode)
+sma_parse_inputs(const struct ocp_selector * const tbl[], const char *buf,
+ enum ptp_ocp_sma_mode *mode)
{
- struct ocp_selector *tbl[] = { ptp_ocp_sma_in, ptp_ocp_sma_out };
int idx, count, dir;
char **argv;
int ret;
@@ -2139,40 +2341,24 @@ out:
return ret;
}
-static u32
-ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr, enum ptp_ocp_sma_mode mode)
-{
- u32 __iomem *gpio;
- u32 shift;
-
- if (bp->sma[sma_nr - 1].fixed_fcn)
- return (sma_nr - 1) & 1;
-
- if (mode == SMA_MODE_IN)
- gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
- else
- gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
- shift = sma_nr & 1 ? 0 : 16;
-
- return (ioread32(gpio) >> shift) & 0xffff;
-}
-
static ssize_t
ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, char *buf,
int default_in_val, int default_out_val)
{
struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+ const struct ocp_selector * const *tbl;
u32 val;
- val = ptp_ocp_sma_get(bp, sma_nr, sma->mode) & SMA_SELECT_MASK;
+ tbl = bp->sma_op->tbl;
+ val = ptp_ocp_sma_get(bp, sma_nr) & SMA_SELECT_MASK;
if (sma->mode == SMA_MODE_IN) {
if (sma->disabled)
val = SMA_DISABLE;
- return ptp_ocp_show_inputs(val, buf, default_in_val);
+ return ptp_ocp_show_inputs(tbl[0], val, buf, default_in_val);
}
- return ptp_ocp_show_output(val, buf, default_out_val);
+ return ptp_ocp_show_output(tbl[1], val, buf, default_out_val);
}
static ssize_t
@@ -2207,54 +2393,6 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
return ptp_ocp_sma_show(bp, 4, buf, -1, 1);
}
-static void
-ptp_ocp_sma_store_output(struct ptp_ocp *bp, int sma_nr, u32 val)
-{
- u32 reg, mask, shift;
- unsigned long flags;
- u32 __iomem *gpio;
-
- gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
- shift = sma_nr & 1 ? 0 : 16;
-
- mask = 0xffff << (16 - shift);
-
- spin_lock_irqsave(&bp->lock, flags);
-
- reg = ioread32(gpio);
- reg = (reg & mask) | (val << shift);
-
- __handle_signal_outputs(bp, reg);
-
- iowrite32(reg, gpio);
-
- spin_unlock_irqrestore(&bp->lock, flags);
-}
-
-static void
-ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
-{
- u32 reg, mask, shift;
- unsigned long flags;
- u32 __iomem *gpio;
-
- gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
- shift = sma_nr & 1 ? 0 : 16;
-
- mask = 0xffff << (16 - shift);
-
- spin_lock_irqsave(&bp->lock, flags);
-
- reg = ioread32(gpio);
- reg = (reg & mask) | (val << shift);
-
- __handle_signal_inputs(bp, reg);
-
- iowrite32(reg, gpio);
-
- spin_unlock_irqrestore(&bp->lock, flags);
-}
-
static int
ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
{
@@ -2263,7 +2401,7 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
int val;
mode = sma->mode;
- val = sma_parse_inputs(buf, &mode);
+ val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
if (val < 0)
return val;
@@ -2271,7 +2409,7 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
return -EOPNOTSUPP;
if (sma->fixed_fcn) {
- if (val != ((sma_nr - 1) & 1))
+ if (val != sma->default_fcn)
return -EOPNOTSUPP;
return 0;
}
@@ -2280,9 +2418,9 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
if (mode != sma->mode) {
if (mode == SMA_MODE_IN)
- ptp_ocp_sma_store_output(bp, sma_nr, 0);
+ ptp_ocp_sma_set_output(bp, sma_nr, 0);
else
- ptp_ocp_sma_store_inputs(bp, sma_nr, 0);
+ ptp_ocp_sma_set_inputs(bp, sma_nr, 0);
sma->mode = mode;
}
@@ -2293,11 +2431,11 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
val = 0;
if (mode == SMA_MODE_IN)
- ptp_ocp_sma_store_inputs(bp, sma_nr, val);
+ val = ptp_ocp_sma_set_inputs(bp, sma_nr, val);
else
- ptp_ocp_sma_store_output(bp, sma_nr, val);
+ val = ptp_ocp_sma_set_output(bp, sma_nr, val);
- return 0;
+ return val;
}
static ssize_t
@@ -2352,7 +2490,9 @@ static ssize_t
available_sma_inputs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return ptp_ocp_select_table_show(ptp_ocp_sma_in, buf);
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+ return ptp_ocp_select_table_show(bp->sma_op->tbl[0], buf);
}
static DEVICE_ATTR_RO(available_sma_inputs);
@@ -2360,7 +2500,9 @@ static ssize_t
available_sma_outputs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return ptp_ocp_select_table_show(ptp_ocp_sma_out, buf);
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+ return ptp_ocp_select_table_show(bp->sma_op->tbl[1], buf);
}
static DEVICE_ATTR_RO(available_sma_outputs);
@@ -3025,10 +3167,10 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
struct device *dev = s->private;
struct ptp_system_timestamp sts;
struct ts_reg __iomem *ts_reg;
+ char *buf, *src, *mac_src;
struct timespec64 ts;
struct ptp_ocp *bp;
u16 sma_val[4][2];
- char *src, *buf;
u32 ctrl, val;
bool on, map;
int i;
@@ -3191,17 +3333,26 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
if (bp->pps_select) {
val = ioread32(&bp->pps_select->gpio1);
src = &buf[80];
- if (val & 0x01)
+ mac_src = "GNSS1";
+ if (val & 0x01) {
gpio_input_map(src, bp, sma_val, 0, NULL);
- else if (val & 0x02)
+ mac_src = src;
+ } else if (val & 0x02) {
src = "MAC";
- else if (val & 0x04)
+ } else if (val & 0x04) {
src = "GNSS1";
- else
+ } else {
src = "----";
+ mac_src = src;
+ }
} else {
src = "?";
+ mac_src = src;
}
+ seq_printf(s, "MAC PPS1 src: %s\n", mac_src);
+
+ gpio_input_map(buf, bp, sma_val, 1, "GNSS2");
+ seq_printf(s, "MAC PPS2 src: %s\n", buf);
/* assumes automatic switchover/selection */
val = ioread32(&bp->reg->select);
@@ -3226,12 +3377,6 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf,
val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced");
- /* reuses PPS1 src from earlier */
- seq_printf(s, "MAC PPS1 src: %s\n", src);
-
- gpio_input_map(buf, bp, sma_val, 1, "GNSS2");
- seq_printf(s, "MAC PPS2 src: %s\n", buf);
-
if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) {
struct timespec64 sys_ts;
s64 pre_ns, post_ns, ns;
@@ -3498,14 +3643,6 @@ ptp_ocp_info(struct ptp_ocp *bp)
ptp_ocp_phc_info(bp);
- dev_info(dev, "version %x\n", bp->fw_version);
- if (bp->fw_version & 0xffff)
- dev_info(dev, "regular image, version %d\n",
- bp->fw_version & 0xffff);
- else
- dev_info(dev, "golden image, version %d\n",
- bp->fw_version >> 16);
-
ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200);
ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200);
ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600);
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index dba6be477067..77918a2c6701 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -52,6 +52,7 @@ struct ptp_clock {
int *vclock_index;
struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */
bool is_virtual_clock;
+ bool has_cycles;
};
#define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
@@ -62,6 +63,7 @@ struct ptp_vclock {
struct ptp_clock *pclock;
struct ptp_clock_info info;
struct ptp_clock *clock;
+ struct hlist_node vclock_hash_node;
struct cyclecounter cc;
struct timecounter tc;
spinlock_t lock; /* protects tc/cc */
@@ -96,6 +98,15 @@ static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
return in_use;
}
+/* Check if ptp clock shall be free running */
+static inline bool ptp_clock_freerun(struct ptp_clock *ptp)
+{
+ if (ptp->has_cycles)
+ return false;
+
+ return ptp_vclock_in_use(ptp);
+}
+
extern struct class *ptp_class;
/*
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index 9233bfedeb17..f30b0a439470 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c
@@ -231,10 +231,13 @@ static ssize_t n_vclocks_store(struct device *dev,
*(ptp->vclock_index + ptp->n_vclocks - i) = -1;
}
- if (num == 0)
- dev_info(dev, "only physical clock in use now\n");
- else
- dev_info(dev, "guarantee physical clock free running\n");
+ /* Need to inform about changed physical clock behavior */
+ if (!ptp->has_cycles) {
+ if (num == 0)
+ dev_info(dev, "only physical clock in use now\n");
+ else
+ dev_info(dev, "guarantee physical clock free running\n");
+ }
ptp->n_vclocks = num;
mutex_unlock(&ptp->n_vclocks_mux);
diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c
index cb179a3ea508..1c0ed4805c0a 100644
--- a/drivers/ptp/ptp_vclock.c
+++ b/drivers/ptp/ptp_vclock.c
@@ -5,6 +5,7 @@
* Copyright 2021 NXP
*/
#include <linux/slab.h>
+#include <linux/hashtable.h>
#include "ptp_private.h"
#define PTP_VCLOCK_CC_SHIFT 31
@@ -13,6 +14,32 @@
#define PTP_VCLOCK_FADJ_DENOMINATOR 15625ULL
#define PTP_VCLOCK_REFRESH_INTERVAL (HZ * 2)
+/* protects vclock_hash addition/deletion */
+static DEFINE_SPINLOCK(vclock_hash_lock);
+
+static DEFINE_READ_MOSTLY_HASHTABLE(vclock_hash, 8);
+
+static void ptp_vclock_hash_add(struct ptp_vclock *vclock)
+{
+ spin_lock(&vclock_hash_lock);
+
+ hlist_add_head_rcu(&vclock->vclock_hash_node,
+ &vclock_hash[vclock->clock->index % HASH_SIZE(vclock_hash)]);
+
+ spin_unlock(&vclock_hash_lock);
+}
+
+static void ptp_vclock_hash_del(struct ptp_vclock *vclock)
+{
+ spin_lock(&vclock_hash_lock);
+
+ hlist_del_init_rcu(&vclock->vclock_hash_node);
+
+ spin_unlock(&vclock_hash_lock);
+
+ synchronize_rcu();
+}
+
static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct ptp_vclock *vclock = info_to_vclock(ptp);
@@ -68,7 +95,7 @@ static int ptp_vclock_gettimex(struct ptp_clock_info *ptp,
int err;
u64 ns;
- err = pptp->info->gettimex64(pptp->info, &pts, sts);
+ err = pptp->info->getcyclesx64(pptp->info, &pts, sts);
if (err)
return err;
@@ -104,7 +131,7 @@ static int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp,
int err;
u64 ns;
- err = pptp->info->getcrosststamp(pptp->info, xtstamp);
+ err = pptp->info->getcrosscycles(pptp->info, xtstamp);
if (err)
return err;
@@ -143,10 +170,7 @@ static u64 ptp_vclock_read(const struct cyclecounter *cc)
struct ptp_clock *ptp = vclock->pclock;
struct timespec64 ts = {};
- if (ptp->info->gettimex64)
- ptp->info->gettimex64(ptp->info, &ts, NULL);
- else
- ptp->info->gettime64(ptp->info, &ts);
+ ptp->info->getcycles64(ptp->info, &ts);
return timespec64_to_ns(&ts);
}
@@ -168,17 +192,19 @@ struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
vclock->pclock = pclock;
vclock->info = ptp_vclock_info;
- if (pclock->info->gettimex64)
+ if (pclock->info->getcyclesx64)
vclock->info.gettimex64 = ptp_vclock_gettimex;
else
vclock->info.gettime64 = ptp_vclock_gettime;
- if (pclock->info->getcrosststamp)
+ if (pclock->info->getcrosscycles)
vclock->info.getcrosststamp = ptp_vclock_getcrosststamp;
vclock->cc = ptp_vclock_cc;
snprintf(vclock->info.name, PTP_CLOCK_NAME_LEN, "ptp%d_virt",
pclock->index);
+ INIT_HLIST_NODE(&vclock->vclock_hash_node);
+
spin_lock_init(&vclock->lock);
vclock->clock = ptp_clock_register(&vclock->info, &pclock->dev);
@@ -190,11 +216,15 @@ struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
timecounter_init(&vclock->tc, &vclock->cc, 0);
ptp_schedule_worker(vclock->clock, PTP_VCLOCK_REFRESH_INTERVAL);
+ ptp_vclock_hash_add(vclock);
+
return vclock;
}
void ptp_vclock_unregister(struct ptp_vclock *vclock)
{
+ ptp_vclock_hash_del(vclock);
+
ptp_clock_unregister(vclock->clock);
kfree(vclock);
}
@@ -235,37 +265,31 @@ out:
}
EXPORT_SYMBOL(ptp_get_vclocks_index);
-ktime_t ptp_convert_timestamp(const struct skb_shared_hwtstamps *hwtstamps,
- int vclock_index)
+ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index)
{
- char name[PTP_CLOCK_NAME_LEN] = "";
+ unsigned int hash = vclock_index % HASH_SIZE(vclock_hash);
struct ptp_vclock *vclock;
- struct ptp_clock *ptp;
unsigned long flags;
- struct device *dev;
u64 ns;
+ u64 vclock_ns = 0;
- snprintf(name, PTP_CLOCK_NAME_LEN, "ptp%d", vclock_index);
- dev = class_find_device_by_name(ptp_class, name);
- if (!dev)
- return 0;
+ ns = ktime_to_ns(*hwtstamp);
- ptp = dev_get_drvdata(dev);
- if (!ptp->is_virtual_clock) {
- put_device(dev);
- return 0;
- }
+ rcu_read_lock();
- vclock = info_to_vclock(ptp->info);
+ hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) {
+ if (vclock->clock->index != vclock_index)
+ continue;
- ns = ktime_to_ns(hwtstamps->hwtstamp);
+ spin_lock_irqsave(&vclock->lock, flags);
+ vclock_ns = timecounter_cyc2time(&vclock->tc, ns);
+ spin_unlock_irqrestore(&vclock->lock, flags);
+ break;
+ }
- spin_lock_irqsave(&vclock->lock, flags);
- ns = timecounter_cyc2time(&vclock->tc, ns);
- spin_unlock_irqrestore(&vclock->lock, flags);
+ rcu_read_unlock();
- put_device(dev);
- return ns_to_ktime(ns);
+ return ns_to_ktime(vclock_ns);
}
EXPORT_SYMBOL(ptp_convert_timestamp);
#endif