OSDN Git Service

net: dsa: sja1105: Restore PTP time after switch reset
authorVladimir Oltean <olteanv@gmail.com>
Sat, 9 Nov 2019 11:32:23 +0000 (13:32 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Nov 2019 20:45:30 +0000 (12:45 -0800)
The PTP time of the switch is not preserved when uploading a new static
configuration. Work around this hardware oddity by reading its PTP time
before a static config upload, and restoring it afterwards.

Static config changes are expected to occur at runtime even in scenarios
directly related to PTP, i.e. the Time-Aware Scheduler of the switch is
programmed in this way.

Perhaps the larger implication of this patch is that the PTP .gettimex64
and .settime functions need to be exposed to sja1105_main.c, where the
PTP lock needs to be held during this entire process. So their core
implementation needs to move to some common functions which get exposed
in sja1105_ptp.h.

Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/dsa/sja1105/sja1105_ptp.c
drivers/net/dsa/sja1105/sja1105_ptp.h
drivers/net/dsa/sja1105/sja1105_spi.c

index d545edb..2b8919a 100644 (file)
@@ -1349,9 +1349,15 @@ static void sja1105_bridge_leave(struct dsa_switch *ds, int port,
  */
 int sja1105_static_config_reload(struct sja1105_private *priv)
 {
+       struct ptp_system_timestamp ptp_sts_before;
+       struct ptp_system_timestamp ptp_sts_after;
        struct sja1105_mac_config_entry *mac;
        int speed_mbps[SJA1105_NUM_PORTS];
+       struct dsa_switch *ds = priv->ds;
+       s64 t1, t2, t3, t4;
+       s64 t12, t34;
        int rc, i;
+       s64 now;
 
        mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
 
@@ -1365,10 +1371,37 @@ int sja1105_static_config_reload(struct sja1105_private *priv)
                mac[i].speed = SJA1105_SPEED_AUTO;
        }
 
+       /* No PTP operations can run right now */
+       mutex_lock(&priv->ptp_data.lock);
+
+       rc = __sja1105_ptp_gettimex(ds, &now, &ptp_sts_before);
+       if (rc < 0)
+               goto out_unlock_ptp;
+
        /* Reset switch and send updated static configuration */
        rc = sja1105_static_config_upload(priv);
        if (rc < 0)
-               goto out;
+               goto out_unlock_ptp;
+
+       rc = __sja1105_ptp_settime(ds, 0, &ptp_sts_after);
+       if (rc < 0)
+               goto out_unlock_ptp;
+
+       t1 = timespec64_to_ns(&ptp_sts_before.pre_ts);
+       t2 = timespec64_to_ns(&ptp_sts_before.post_ts);
+       t3 = timespec64_to_ns(&ptp_sts_after.pre_ts);
+       t4 = timespec64_to_ns(&ptp_sts_after.post_ts);
+       /* Mid point, corresponds to pre-reset PTPCLKVAL */
+       t12 = t1 + (t2 - t1) / 2;
+       /* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */
+       t34 = t3 + (t4 - t3) / 2;
+       /* Advance PTPCLKVAL by the time it took since its readout */
+       now += (t34 - t12);
+
+       __sja1105_ptp_adjtime(ds, now);
+
+out_unlock_ptp:
+       mutex_unlock(&priv->ptp_data.lock);
 
        /* Configure the CGU (PLLs) for MII and RMII PHYs.
         * For these interfaces there is no dynamic configuration
index fac72af..0a35813 100644 (file)
@@ -346,12 +346,13 @@ static int sja1105_ptpclkval_read(struct sja1105_private *priv, u64 *ticks,
 }
 
 /* Caller must hold ptp_data->lock */
-static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks)
+static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks,
+                                  struct ptp_system_timestamp *ptp_sts)
 {
        const struct sja1105_regs *regs = priv->info->regs;
 
        return sja1105_xfer_u64(priv, SPI_WRITE, regs->ptpclkval, &ticks,
-                               NULL);
+                               ptp_sts);
 }
 
 #define rxtstamp_to_tagger(d) \
@@ -427,7 +428,7 @@ bool sja1105_port_txtstamp(struct dsa_switch *ds, int port,
        return true;
 }
 
-int sja1105_ptp_reset(struct dsa_switch *ds)
+static int sja1105_ptp_reset(struct dsa_switch *ds)
 {
        struct sja1105_private *priv = ds->priv;
        struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
@@ -445,19 +446,38 @@ int sja1105_ptp_reset(struct dsa_switch *ds)
        return rc;
 }
 
+/* Caller must hold ptp_data->lock */
+int __sja1105_ptp_gettimex(struct dsa_switch *ds, u64 *ns,
+                          struct ptp_system_timestamp *ptp_sts)
+{
+       struct sja1105_private *priv = ds->priv;
+       u64 ticks;
+       int rc;
+
+       rc = sja1105_ptpclkval_read(priv, &ticks, ptp_sts);
+       if (rc < 0) {
+               dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
+               return rc;
+       }
+
+       *ns = sja1105_ticks_to_ns(ticks);
+
+       return 0;
+}
+
 static int sja1105_ptp_gettimex(struct ptp_clock_info *ptp,
                                struct timespec64 *ts,
                                struct ptp_system_timestamp *ptp_sts)
 {
        struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
        struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
-       u64 ticks = 0;
+       u64 now = 0;
        int rc;
 
        mutex_lock(&ptp_data->lock);
 
-       rc = sja1105_ptpclkval_read(priv, &ticks, ptp_sts);
-       *ts = ns_to_timespec64(sja1105_ticks_to_ns(ticks));
+       rc = __sja1105_ptp_gettimex(priv->ds, &now, ptp_sts);
+       *ts = ns_to_timespec64(now);
 
        mutex_unlock(&ptp_data->lock);
 
@@ -479,24 +499,34 @@ static int sja1105_ptp_mode_set(struct sja1105_private *priv,
 }
 
 /* Write to PTPCLKVAL while PTPCLKADD is 0 */
+int __sja1105_ptp_settime(struct dsa_switch *ds, u64 ns,
+                         struct ptp_system_timestamp *ptp_sts)
+{
+       struct sja1105_private *priv = ds->priv;
+       u64 ticks = ns_to_sja1105_ticks(ns);
+       int rc;
+
+       rc = sja1105_ptp_mode_set(priv, PTP_SET_MODE);
+       if (rc < 0) {
+               dev_err(priv->ds->dev, "Failed to put PTPCLK in set mode\n");
+               return rc;
+       }
+
+       return sja1105_ptpclkval_write(priv, ticks, ptp_sts);
+}
+
 static int sja1105_ptp_settime(struct ptp_clock_info *ptp,
                               const struct timespec64 *ts)
 {
        struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
        struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
-       u64 ticks = ns_to_sja1105_ticks(timespec64_to_ns(ts));
+       u64 ns = timespec64_to_ns(ts);
        int rc;
 
        mutex_lock(&ptp_data->lock);
 
-       rc = sja1105_ptp_mode_set(priv, PTP_SET_MODE);
-       if (rc < 0) {
-               dev_err(priv->ds->dev, "Failed to put PTPCLK in set mode\n");
-               goto out;
-       }
+       rc = __sja1105_ptp_settime(priv->ds, ns, NULL);
 
-       rc = sja1105_ptpclkval_write(priv, ticks);
-out:
        mutex_unlock(&ptp_data->lock);
 
        return rc;
@@ -530,24 +560,31 @@ static int sja1105_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 }
 
 /* Write to PTPCLKVAL while PTPCLKADD is 1 */
-static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+int __sja1105_ptp_adjtime(struct dsa_switch *ds, s64 delta)
 {
-       struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
-       struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
+       struct sja1105_private *priv = ds->priv;
        s64 ticks = ns_to_sja1105_ticks(delta);
        int rc;
 
-       mutex_lock(&ptp_data->lock);
-
        rc = sja1105_ptp_mode_set(priv, PTP_ADD_MODE);
        if (rc < 0) {
                dev_err(priv->ds->dev, "Failed to put PTPCLK in add mode\n");
-               goto out;
+               return rc;
        }
 
-       rc = sja1105_ptpclkval_write(priv, ticks);
+       return sja1105_ptpclkval_write(priv, ticks, NULL);
+}
+
+static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+       struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
+       int rc;
+
+       mutex_lock(&ptp_data->lock);
+
+       rc = __sja1105_ptp_adjtime(priv->ds, delta);
 
-out:
        mutex_unlock(&ptp_data->lock);
 
        return rc;
index 243f130..19e707d 100644 (file)
@@ -51,8 +51,6 @@ int sja1105_get_ts_info(struct dsa_switch *ds, int port,
 void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot,
                              struct sk_buff *clone);
 
-int sja1105_ptp_reset(struct dsa_switch *ds);
-
 bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
                           struct sk_buff *skb, unsigned int type);
 
@@ -63,6 +61,14 @@ int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
 
 int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
 
+int __sja1105_ptp_gettimex(struct dsa_switch *ds, u64 *ns,
+                          struct ptp_system_timestamp *sts);
+
+int __sja1105_ptp_settime(struct dsa_switch *ds, u64 ns,
+                         struct ptp_system_timestamp *ptp_sts);
+
+int __sja1105_ptp_adjtime(struct dsa_switch *ds, s64 delta);
+
 #else
 
 struct sja1105_ptp_cmd;
@@ -87,7 +93,19 @@ static inline void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot,
 {
 }
 
-static inline int sja1105_ptp_reset(struct dsa_switch *ds)
+static inline int __sja1105_ptp_gettimex(struct dsa_switch *ds, u64 *ns,
+                                        struct ptp_system_timestamp *sts)
+{
+       return 0;
+}
+
+static inline int __sja1105_ptp_settime(struct dsa_switch *ds, u64 ns,
+                                       struct ptp_system_timestamp *ptp_sts)
+{
+       return 0;
+}
+
+static inline int __sja1105_ptp_adjtime(struct dsa_switch *ds, s64 delta)
 {
        return 0;
 }
index 691cd25..cb48e77 100644 (file)
@@ -511,12 +511,6 @@ int sja1105_static_config_upload(struct sja1105_private *priv)
                dev_info(dev, "Succeeded after %d tried\n", RETRIES - retries);
        }
 
-       rc = sja1105_ptp_reset(priv->ds);
-       if (rc < 0)
-               dev_err(dev, "Failed to reset PTP clock: %d\n", rc);
-
-       dev_info(dev, "Reset switch and programmed static config\n");
-
 out:
        kfree(config_buf);
        return rc;