}
 EXPORT_SYMBOL(mlxsw_core_skb_transmit);
 
+void mlxsw_core_ptp_transmitted(struct mlxsw_core *mlxsw_core,
+                               struct sk_buff *skb, u8 local_port)
+{
+       if (mlxsw_core->driver->ptp_transmitted)
+               mlxsw_core->driver->ptp_transmitted(mlxsw_core, skb,
+                                                   local_port);
+}
+EXPORT_SYMBOL(mlxsw_core_ptp_transmitted);
+
 static bool __is_rx_listener_equal(const struct mlxsw_rx_listener *rxl_a,
                                   const struct mlxsw_rx_listener *rxl_b)
 {
 
                                  const struct mlxsw_tx_info *tx_info);
 int mlxsw_core_skb_transmit(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
                            const struct mlxsw_tx_info *tx_info);
+void mlxsw_core_ptp_transmitted(struct mlxsw_core *mlxsw_core,
+                               struct sk_buff *skb, u8 local_port);
 
 struct mlxsw_rx_listener {
        void (*func)(struct sk_buff *skb, u8 local_port, void *priv);
                             u64 *p_linear_size);
        int (*params_register)(struct mlxsw_core *mlxsw_core);
        void (*params_unregister)(struct mlxsw_core *mlxsw_core);
+
+       /* Notify a driver that a timestamped packet was transmitted. Driver
+        * is responsible for freeing the passed-in SKB.
+        */
+       void (*ptp_transmitted)(struct mlxsw_core *mlxsw_core,
+                               struct sk_buff *skb, u8 local_port);
+
        u8 txhdr_len;
        const struct mlxsw_config_profile *profile;
        bool res_query_enabled;
 };
 
 struct mlxsw_skb_cb {
+       struct mlxsw_tx_info tx_info;
 };
 
 static inline struct mlxsw_skb_cb *mlxsw_skb_cb(struct sk_buff *skb)
 
 {
        struct pci_dev *pdev = mlxsw_pci->pdev;
        struct mlxsw_pci_queue_elem_info *elem_info;
+       struct mlxsw_tx_info tx_info;
        char *wqe;
        struct sk_buff *skb;
        int i;
 
        spin_lock(&q->lock);
        elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
+       tx_info = mlxsw_skb_cb(elem_info->u.sdq.skb)->tx_info;
        skb = elem_info->u.sdq.skb;
        wqe = elem_info->elem;
        for (i = 0; i < MLXSW_PCI_WQE_SG_ENTRIES; i++)
                mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, i, DMA_TO_DEVICE);
-       dev_kfree_skb_any(skb);
+
+       if (unlikely(!tx_info.is_emad &&
+                    skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+               mlxsw_core_ptp_transmitted(mlxsw_pci->core, skb,
+                                          tx_info.local_port);
+               skb = NULL;
+       }
+
+       if (skb)
+               dev_kfree_skb_any(skb);
        elem_info->u.sdq.skb = NULL;
 
        if (q->consumer_counter++ != consumer_counter_limit)
                err = -EAGAIN;
                goto unlock;
        }
+       mlxsw_skb_cb(skb)->tx_info = *tx_info;
        elem_info->u.sdq.skb = skb;
 
        wqe = elem_info->elem;
                        goto unmap_frags;
        }
 
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
        /* Set unused sq entries byte count to zero. */
        for (i++; i < MLXSW_PCI_WQE_SG_ENTRIES; i++)
                mlxsw_pci_wqe_byte_count_set(wqe, i, 0);
 
         */
        void (*receive)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
                        u8 local_port);
+
+       /* Notify a driver that a timestamped packet was transmitted. Driver
+        * is responsible for freeing the passed-in SKB.
+        */
+       void (*transmitted)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
+                           u8 local_port);
 };
 
 static int mlxsw_sp_component_query(struct mlxfw_dev *mlxfw_dev,
        .clock_init     = mlxsw_sp1_ptp_clock_init,
        .clock_fini     = mlxsw_sp1_ptp_clock_fini,
        .receive        = mlxsw_sp1_ptp_receive,
+       .transmitted    = mlxsw_sp1_ptp_transmitted,
 };
 
 static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
        .clock_init     = mlxsw_sp2_ptp_clock_init,
        .clock_fini     = mlxsw_sp2_ptp_clock_fini,
        .receive        = mlxsw_sp2_ptp_receive,
+       .transmitted    = mlxsw_sp2_ptp_transmitted,
 };
 
 static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
        mlxsw_sp_params_unregister(mlxsw_core);
 }
 
+static void mlxsw_sp_ptp_transmitted(struct mlxsw_core *mlxsw_core,
+                                    struct sk_buff *skb, u8 local_port)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
+
+       skb_pull(skb, MLXSW_TXHDR_LEN);
+       mlxsw_sp->ptp_ops->transmitted(mlxsw_sp, skb, local_port);
+}
+
 static struct mlxsw_driver mlxsw_sp1_driver = {
        .kind                           = mlxsw_sp1_driver_name,
        .priv_size                      = sizeof(struct mlxsw_sp),
        .kvd_sizes_get                  = mlxsw_sp_kvd_sizes_get,
        .params_register                = mlxsw_sp_params_register,
        .params_unregister              = mlxsw_sp_params_unregister,
+       .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp1_config_profile,
        .res_query_enabled              = true,
        .resources_register             = mlxsw_sp2_resources_register,
        .params_register                = mlxsw_sp2_params_register,
        .params_unregister              = mlxsw_sp2_params_unregister,
+       .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp2_config_profile,
        .res_query_enabled              = true,
 
 {
        mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp);
 }
+
+void mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
+                              struct sk_buff *skb, u8 local_port)
+{
+       dev_kfree_skb_any(skb);
+}
 
 void mlxsw_sp1_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
                           u8 local_port);
 
+void mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
+                              struct sk_buff *skb, u8 local_port);
+
 #else
 
 static inline struct mlxsw_sp_ptp_clock *
        mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp);
 }
 
+static inline void mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
+                                            struct sk_buff *skb, u8 local_port)
+{
+       dev_kfree_skb_any(skb);
+}
+
 #endif
 
 static inline struct mlxsw_sp_ptp_clock *
        mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp);
 }
 
+static inline void mlxsw_sp2_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
+                                            struct sk_buff *skb, u8 local_port)
+{
+       dev_kfree_skb_any(skb);
+}
+
 #endif