diff options
Diffstat (limited to 'drivers/ptp')
-rw-r--r-- | drivers/ptp/ptp_clock.c | 31 | ||||
-rw-r--r-- | drivers/ptp/ptp_clockmatrix.c | 321 | ||||
-rw-r--r-- | drivers/ptp/ptp_clockmatrix.h | 7 | ||||
-rw-r--r-- | drivers/ptp/ptp_ocp.c | 559 | ||||
-rw-r--r-- | drivers/ptp/ptp_private.h | 11 | ||||
-rw-r--r-- | drivers/ptp/ptp_sysfs.c | 11 | ||||
-rw-r--r-- | drivers/ptp/ptp_vclock.c | 82 |
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 |