sja1105_spi_rw_mode_t rw, u64 reg_addr,
                     u8 *buf, size_t len);
 int sja1105_xfer_u32(const struct sja1105_private *priv,
-                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value);
+                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value,
+                    struct ptp_system_timestamp *ptp_sts);
 int sja1105_xfer_u64(const struct sja1105_private *priv,
-                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value);
+                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value,
+                    struct ptp_system_timestamp *ptp_sts);
 int sja1105_static_config_upload(struct sja1105_private *priv);
 int sja1105_inhibit_tx(const struct sja1105_private *priv,
                       unsigned long port_bitmap, bool tx_inhibited);
 
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
  */
+#include <linux/spi/spi.h>
 #include "sja1105.h"
 
 /* The adjfine API clamps ppb between [-32,768,000, 32,768,000], and
 }
 
 /* Caller must hold ptp_data->lock */
-static int sja1105_ptpclkval_read(struct sja1105_private *priv, u64 *ticks)
+static int sja1105_ptpclkval_read(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_READ, regs->ptpclkval, ticks);
+       return sja1105_xfer_u64(priv, SPI_READ, regs->ptpclkval, ticks,
+                               ptp_sts);
 }
 
 /* Caller must hold ptp_data->lock */
 {
        const struct sja1105_regs *regs = priv->info->regs;
 
-       return sja1105_xfer_u64(priv, SPI_WRITE, regs->ptpclkval, &ticks);
+       return sja1105_xfer_u64(priv, SPI_WRITE, regs->ptpclkval, &ticks,
+                               NULL);
 }
 
 #define rxtstamp_to_tagger(d) \
                u64 ticks, ts;
                int rc;
 
-               rc = sja1105_ptpclkval_read(priv, &ticks);
+               rc = sja1105_ptpclkval_read(priv, &ticks, NULL);
                if (rc < 0) {
                        dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
                        kfree_skb(skb);
        return rc;
 }
 
-static int sja1105_ptp_gettime(struct ptp_clock_info *ptp,
-                              struct timespec64 *ts)
+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);
 
        mutex_lock(&ptp_data->lock);
 
-       rc = sja1105_ptpclkval_read(priv, &ticks);
+       rc = sja1105_ptpclkval_read(priv, &ticks, ptp_sts);
        *ts = ns_to_timespec64(sja1105_ticks_to_ns(ticks));
 
        mutex_unlock(&ptp_data->lock);
 
        mutex_lock(&ptp_data->lock);
 
-       rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptpclkrate, &clkrate32);
+       rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptpclkrate, &clkrate32,
+                             NULL);
 
        mutex_unlock(&ptp_data->lock);
 
                .name           = "SJA1105 PHC",
                .adjfine        = sja1105_ptp_adjfine,
                .adjtime        = sja1105_ptp_adjtime,
-               .gettime64      = sja1105_ptp_gettime,
+               .gettimex64     = sja1105_ptp_gettimex,
                .settime64      = sja1105_ptp_settime,
                .max_adj        = SJA1105_MAX_ADJ_PPB,
        };
 
        mutex_lock(&ptp_data->lock);
 
-       rc = sja1105_ptpclkval_read(priv, &ticks);
+       rc = sja1105_ptpclkval_read(priv, &ticks, NULL);
        if (rc < 0) {
                dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
                kfree_skb(skb);
 
  * - SPI_READ:  creates and sends an SPI read message from absolute
  *             address reg_addr, writing @len bytes into *buf
  */
-int sja1105_xfer_buf(const struct sja1105_private *priv,
-                    sja1105_spi_rw_mode_t rw, u64 reg_addr,
-                    u8 *buf, size_t len)
+static int sja1105_xfer(const struct sja1105_private *priv,
+                       sja1105_spi_rw_mode_t rw, u64 reg_addr, u8 *buf,
+                       size_t len, struct ptp_system_timestamp *ptp_sts)
 {
        struct sja1105_chunk chunk = {
                .len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
                struct spi_transfer *chunk_xfer = sja1105_chunk_xfer(xfers, i);
                struct spi_transfer *hdr_xfer = sja1105_hdr_xfer(xfers, i);
                u8 *hdr_buf = sja1105_hdr_buf(hdr_bufs, i);
+               struct spi_transfer *ptp_sts_xfer;
                struct sja1105_spi_message msg;
 
                /* Populate the transfer's header buffer */
                        chunk_xfer->tx_buf = chunk.buf;
                chunk_xfer->len = chunk.len;
 
+               /* Request timestamping for the transfer. Instead of letting
+                * callers specify which byte they want to timestamp, we can
+                * make certain assumptions:
+                * - A read operation will request a software timestamp when
+                *   what's being read is the PTP time. That is snapshotted by
+                *   the switch hardware at the end of the command portion
+                *   (hdr_xfer).
+                * - A write operation will request a software timestamp on
+                *   actions that modify the PTP time. Taking clock stepping as
+                *   an example, the switch writes the PTP time at the end of
+                *   the data portion (chunk_xfer).
+                */
+               if (rw == SPI_READ)
+                       ptp_sts_xfer = hdr_xfer;
+               else
+                       ptp_sts_xfer = chunk_xfer;
+               ptp_sts_xfer->ptp_sts_word_pre = ptp_sts_xfer->len - 1;
+               ptp_sts_xfer->ptp_sts_word_post = ptp_sts_xfer->len - 1;
+               ptp_sts_xfer->ptp_sts = ptp_sts;
+
                /* Calculate next chunk */
                chunk.buf += chunk.len;
                chunk.reg_addr += chunk.len / 4;
        return rc;
 }
 
+int sja1105_xfer_buf(const struct sja1105_private *priv,
+                    sja1105_spi_rw_mode_t rw, u64 reg_addr,
+                    u8 *buf, size_t len)
+{
+       return sja1105_xfer(priv, rw, reg_addr, buf, len, NULL);
+}
+
 /* If @rw is:
  * - SPI_WRITE: creates and sends an SPI write message at absolute
  *             address reg_addr
  * CPU endianness and directly usable by software running on the core.
  */
 int sja1105_xfer_u64(const struct sja1105_private *priv,
-                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value)
+                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value,
+                    struct ptp_system_timestamp *ptp_sts)
 {
        u8 packed_buf[8];
        int rc;
        if (rw == SPI_WRITE)
                sja1105_pack(packed_buf, value, 63, 0, 8);
 
-       rc = sja1105_xfer_buf(priv, rw, reg_addr, packed_buf, 8);
+       rc = sja1105_xfer(priv, rw, reg_addr, packed_buf, 8, ptp_sts);
 
        if (rw == SPI_READ)
                sja1105_unpack(packed_buf, value, 63, 0, 8);
 
 /* Same as above, but transfers only a 4 byte word */
 int sja1105_xfer_u32(const struct sja1105_private *priv,
-                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value)
+                    sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value,
+                    struct ptp_system_timestamp *ptp_sts)
 {
        u8 packed_buf[4];
        u64 tmp;
                sja1105_pack(packed_buf, &tmp, 31, 0, 4);
        }
 
-       rc = sja1105_xfer_buf(priv, rw, reg_addr, packed_buf, 4);
+       rc = sja1105_xfer(priv, rw, reg_addr, packed_buf, 4, ptp_sts);
 
        if (rw == SPI_READ) {
                sja1105_unpack(packed_buf, &tmp, 31, 0, 4);
        int rc;
 
        rc = sja1105_xfer_u32(priv, SPI_READ, regs->port_control,
-                             &inhibit_cmd);
+                             &inhibit_cmd, NULL);
        if (rc < 0)
                return rc;
 
                inhibit_cmd &= ~port_bitmap;
 
        return sja1105_xfer_u32(priv, SPI_WRITE, regs->port_control,
-                               &inhibit_cmd);
+                               &inhibit_cmd, NULL);
 }
 
 struct sja1105_status {