};
 
 struct packet_type;
+struct dsa_switch;
 
 struct dsa_device_ops {
        struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev);
                                     struct ifreq *ifr);
        int     (*port_hwtstamp_set)(struct dsa_switch *ds, int port,
                                     struct ifreq *ifr);
+       bool    (*port_txtstamp)(struct dsa_switch *ds, int port,
+                                struct sk_buff *clone, unsigned int type);
+       bool    (*port_rxtstamp)(struct dsa_switch *ds, int port,
+                                struct sk_buff *skb, unsigned int type);
 };
 
 struct dsa_switch_driver {
 
 #include <linux/netdevice.h>
 #include <linux/sysfs.h>
 #include <linux/phy_fixed.h>
+#include <linux/ptp_classify.h>
 #include <linux/gpio/consumer.h>
 #include <linux/etherdevice.h>
 
 }
 EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
 
+/* Determine if we should defer delivery of skb until we have a rx timestamp.
+ *
+ * Called from dsa_switch_rcv. For now, this will only work if tagging is
+ * enabled on the switch. Normally the MAC driver would retrieve the hardware
+ * timestamp when it reads the packet out of the hardware. However in a DSA
+ * switch, the DSA driver owning the interface to which the packet is
+ * delivered is never notified unless we do so here.
+ */
+static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p,
+                                      struct sk_buff *skb)
+{
+       struct dsa_switch *ds = p->dp->ds;
+       unsigned int type;
+
+       if (skb_headroom(skb) < ETH_HLEN)
+               return false;
+
+       __skb_push(skb, ETH_HLEN);
+
+       type = ptp_classify_raw(skb);
+
+       __skb_pull(skb, ETH_HLEN);
+
+       if (type == PTP_CLASS_NONE)
+               return false;
+
+       if (likely(ds->ops->port_rxtstamp))
+               return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type);
+
+       return false;
+}
+
 static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
                          struct packet_type *pt, struct net_device *unused)
 {
        s->rx_bytes += skb->len;
        u64_stats_update_end(&s->syncp);
 
+       if (dsa_skb_defer_rx_timestamp(p, skb))
+               return 0;
+
        netif_receive_skb(skb);
 
        return 0;
 
 #include <net/tc_act/tc_mirred.h>
 #include <linux/if_bridge.h>
 #include <linux/netpoll.h>
+#include <linux/ptp_classify.h>
 
 #include "dsa_priv.h"
 
        return NETDEV_TX_OK;
 }
 
+static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
+                                struct sk_buff *skb)
+{
+       struct dsa_switch *ds = p->dp->ds;
+       struct sk_buff *clone;
+       unsigned int type;
+
+       type = ptp_classify_raw(skb);
+       if (type == PTP_CLASS_NONE)
+               return;
+
+       if (!ds->ops->port_txtstamp)
+               return;
+
+       clone = skb_clone_sk(skb);
+       if (!clone)
+               return;
+
+       if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type))
+               return;
+
+       kfree_skb(clone);
+}
+
 static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
        s->tx_bytes += skb->len;
        u64_stats_update_end(&s->syncp);
 
+       /* Identify PTP protocol packets, clone them, and pass them to the
+        * switch driver
+        */
+       dsa_skb_tx_timestamp(p, skb);
+
        /* Transmit function may have to reallocate the original SKB,
         * in which case it must have freed it. Only free it here on error.
         */