ptp: ptp_clockmatrix: Add PTP_CLK_REQ_EXTTS support
authorMin Li <min.li.xe@renesas.com>
Mon, 16 May 2022 14:47:06 +0000 (10:47 -0400)
committerJakub Kicinski <kuba@kernel.org>
Wed, 18 May 2022 00:49:04 +0000 (17:49 -0700)
Use TOD_READ_SECONDARY for extts to keep TOD_READ_PRIMARY
for gettime and settime exclusively. Before this change,
TOD_READ_PRIMARY was used for both extts and gettime/settime,
which would result in changing TOD read/write triggers between
operations. Using TOD_READ_SECONDARY would make extts
independent of gettime/settime operation

Signed-off-by: Min Li <min.li.xe@renesas.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Link: https://lore.kernel.org/r/1652712427-14703-1-git-send-email-min.li.xe@renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/ptp/ptp_clockmatrix.c
drivers/ptp/ptp_clockmatrix.h
include/linux/mfd/idt8a340_reg.h

index 08e429a0692218780aefbdcbbc501d9806564092..25075764eea43405506dccb53acfb4506814ccc8 100644 (file)
@@ -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;
+
+       idtcm = channel->idtcm;
+       old_mask = idtcm->extts_mask;
 
-       if (todn >= MAX_TOD)
+       /* 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;
-}
-
-static u8 idtcm_enable_extts_mask(struct idtcm_channel *channel,
-                                   u8 extts_mask, bool enable)
-{
-       struct idtcm *idtcm = channel->idtcm;
-       int i, err;
+       err = _idtcm_gettime_triggered(extts_channel, &ts);
+       if (err)
+               return err;
 
-       for (i = 0; i < MAX_TOD; i++) {
-               u8 mask = 1 << i;
-               u8 refn = idtcm->channel[i].refn;
-
-               if (extts_mask & mask) {
-                       /* check extts before disabling it */
-                       if (enable == false) {
-                               err = idtcm_extts_check_channel(idtcm, i);
-                               /* trigger happened so we won't re-enable it */
-                               if (err == 0)
-                                       extts_mask &= ~mask;
-                       }
-                       (void)idtcm_enable_extts(channel, i, refn, enable);
-               }
-       }
+       /* 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);
 
-       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,
@@ -1702,6 +1722,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
@@ -1958,8 +1981,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 +2004,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 +2053,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 +2090,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 +2209,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 +2224,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 +2261,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 +2289,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 +2354,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;
+
+               err = idtcm_extts_check_channel(idtcm, i);
 
-               if (idtcm->extts_mask & mask) {
-                       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 +2408,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 +2491,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;
index 0f3059ae1fffa79e4344901304ef0bb409ed4290..43796508031f1b02b1a8187ace4055f46016d4dd 100644 (file)
 
 #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)
 
@@ -90,6 +92,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 +108,7 @@ struct idtcm_channel {
        /* last input trigger for extts */
        u8                      refn;
        u8                      pll;
+       u8                      tod;
        u16                     output_mask;
 };
 
@@ -116,6 +120,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];
index a18c1539a152a0e0075b37c10cc45c90ed33f95d..0c706085c205cfc64221e54acd105c932305c6d3 100644 (file)
 #define TOD_READ_PRIMARY_0                0xcc40
 #define TOD_READ_PRIMARY_0_V520           0xcc50
 /* 8-bit subns, 32-bit ns, 48-bit seconds */
-#define TOD_READ_PRIMARY                  0x0000
+#define TOD_READ_PRIMARY_BASE             0x0000
 /* Counter increments after TOD write is completed */
 #define TOD_READ_PRIMARY_COUNTER          0x000b
 /* Read trigger configuration */
 
 #define TOD_READ_SECONDARY_0              0xcc90
 #define TOD_READ_SECONDARY_0_V520         0xcca0
+/* 8-bit subns, 32-bit ns, 48-bit seconds */
+#define TOD_READ_SECONDARY_BASE           0x0000
+/* Counter increments after TOD write is completed */
+#define TOD_READ_SECONDARY_COUNTER        0x000b
+/* Read trigger configuration */
+#define TOD_READ_SECONDARY_SEL_CFG_0      0x000c
+/* Read trigger selection */
+#define TOD_READ_SECONDARY_CMD            0x000e
+#define TOD_READ_SECONDARY_CMD_V520       0x000f
+
 #define TOD_READ_SECONDARY_1              0xcca0
 #define TOD_READ_SECONDARY_1_V520         0xccb0
 #define TOD_READ_SECONDARY_2              0xccb0