#include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/vmalloc.h>
+#include <linux/ptp_classify.h>
 #include <net/pkt_sched.h>
 
 static struct sk_buff *enetc_tx_swbd_get_skb(struct enetc_tx_swbd *tx_swbd)
        enetc_wr_reg_hot(tx_ring->tpir, tx_ring->next_to_use);
 }
 
+static int enetc_ptp_parse(struct sk_buff *skb, u8 *udp,
+                          u8 *msgtype, u8 *twostep,
+                          u16 *correction_offset, u16 *body_offset)
+{
+       unsigned int ptp_class;
+       struct ptp_header *hdr;
+       unsigned int type;
+       u8 *base;
+
+       ptp_class = ptp_classify_raw(skb);
+       if (ptp_class == PTP_CLASS_NONE)
+               return -EINVAL;
+
+       hdr = ptp_parse_header(skb, ptp_class);
+       if (!hdr)
+               return -EINVAL;
+
+       type = ptp_class & PTP_CLASS_PMASK;
+       if (type == PTP_CLASS_IPV4 || type == PTP_CLASS_IPV6)
+               *udp = 1;
+       else
+               *udp = 0;
+
+       *msgtype = ptp_get_msgtype(hdr, ptp_class);
+       *twostep = hdr->flag_field[0] & 0x2;
+
+       base = skb_mac_header(skb);
+       *correction_offset = (u8 *)&hdr->correction - base;
+       *body_offset = (u8 *)hdr + sizeof(struct ptp_header) - base;
+
+       return 0;
+}
+
 static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 {
+       bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
+       struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
+       struct enetc_hw *hw = &priv->si->hw;
        struct enetc_tx_swbd *tx_swbd;
-       skb_frag_t *frag;
        int len = skb_headlen(skb);
        union enetc_tx_bd temp_bd;
+       u8 msgtype, twostep, udp;
        union enetc_tx_bd *txbd;
-       bool do_vlan, do_tstamp;
+       u16 offset1, offset2;
        int i, count = 0;
+       skb_frag_t *frag;
        unsigned int f;
        dma_addr_t dma;
        u8 flags = 0;
        count++;
 
        do_vlan = skb_vlan_tag_present(skb);
-       do_tstamp = (skb->cb[0] & ENETC_F_TX_TSTAMP) &&
-                   (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP);
-       tx_swbd->do_tstamp = do_tstamp;
-       tx_swbd->check_wb = tx_swbd->do_tstamp;
+       if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+               if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep, &offset1,
+                                   &offset2) ||
+                   msgtype != PTP_MSGTYPE_SYNC || twostep)
+                       WARN_ONCE(1, "Bad packet for one-step timestamping\n");
+               else
+                       do_onestep_tstamp = true;
+       } else if (skb->cb[0] & ENETC_F_TX_TSTAMP) {
+               do_twostep_tstamp = true;
+       }
+
+       tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
+       tx_swbd->check_wb = tx_swbd->do_twostep_tstamp;
 
-       if (do_vlan || do_tstamp)
+       if (do_vlan || do_onestep_tstamp || do_twostep_tstamp)
                flags |= ENETC_TXBD_FLAGS_EX;
 
        if (tx_ring->tsd_enable)
                        e_flags |= ENETC_TXBD_E_FLAGS_VLAN_INS;
                }
 
-               if (do_tstamp) {
+               if (do_onestep_tstamp) {
+                       u32 lo, hi, val;
+                       u64 sec, nsec;
+                       u8 *data;
+
+                       lo = enetc_rd_hot(hw, ENETC_SICTR0);
+                       hi = enetc_rd_hot(hw, ENETC_SICTR1);
+                       sec = (u64)hi << 32 | lo;
+                       nsec = do_div(sec, 1000000000);
+
+                       /* Configure extension BD */
+                       temp_bd.ext.tstamp = cpu_to_le32(lo & 0x3fffffff);
+                       e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
+
+                       /* Update originTimestamp field of Sync packet
+                        * - 48 bits seconds field
+                        * - 32 bits nanseconds field
+                        */
+                       data = skb_mac_header(skb);
+                       *(__be16 *)(data + offset2) =
+                               htons((sec >> 32) & 0xffff);
+                       *(__be32 *)(data + offset2 + 2) =
+                               htonl(sec & 0xffffffff);
+                       *(__be32 *)(data + offset2 + 6) = htonl(nsec);
+
+                       /* Configure single-step register */
+                       val = ENETC_PM0_SINGLE_STEP_EN;
+                       val |= ENETC_SET_SINGLE_STEP_OFFSET(offset1);
+                       if (udp)
+                               val |= ENETC_PM0_SINGLE_STEP_CH;
+
+                       enetc_port_wr(hw, ENETC_PM0_SINGLE_STEP, val);
+                       enetc_port_wr(hw, ENETC_PM1_SINGLE_STEP, val);
+               } else if (do_twostep_tstamp) {
                        skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
                        e_flags |= ENETC_TXBD_E_FLAGS_TWO_STEP_PTP;
                }
        return 0;
 }
 
-netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
+                                   struct net_device *ndev)
 {
        struct enetc_ndev_priv *priv = netdev_priv(ndev);
        struct enetc_bdr *tx_ring;
        int count;
 
-       /* cb[0] used for TX timestamp type */
-       skb->cb[0] = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
-
        tx_ring = priv->tx_ring[skb->queue_mapping];
 
        if (unlikely(skb_shinfo(skb)->nr_frags > ENETC_MAX_SKB_FRAGS))
        return NETDEV_TX_OK;
 }
 
+netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       u8 udp, msgtype, twostep;
+       u16 offset1, offset2;
+
+       /* Mark tx timestamp type on skb->cb[0] if requires */
+       if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+           (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK)) {
+               skb->cb[0] = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
+       } else {
+               skb->cb[0] = 0;
+       }
+
+       /* Fall back to two-step timestamp if not one-step Sync packet */
+       if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+               if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep,
+                                   &offset1, &offset2) ||
+                   msgtype != PTP_MSGTYPE_SYNC || twostep != 0)
+                       skb->cb[0] = ENETC_F_TX_TSTAMP;
+       }
+
+       /* Queue one-step Sync packet if already locked */
+       if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+               if (test_and_set_bit_lock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS,
+                                         &priv->flags)) {
+                       skb_queue_tail(&priv->tx_skbs, skb);
+                       return NETDEV_TX_OK;
+               }
+       }
+
+       return enetc_start_xmit(skb, ndev);
+}
+
 static irqreturn_t enetc_msix(int irq, void *data)
 {
        struct enetc_int_vector *v = data;
 static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
 {
        struct net_device *ndev = tx_ring->ndev;
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
        int tx_frm_cnt = 0, tx_byte_cnt = 0;
        struct enetc_tx_swbd *tx_swbd;
        int i, bds_to_clean;
-       bool do_tstamp;
+       bool do_twostep_tstamp;
        u64 tstamp = 0;
 
        i = tx_ring->next_to_clean;
 
        bds_to_clean = enetc_bd_ready_count(tx_ring, i);
 
-       do_tstamp = false;
+       do_twostep_tstamp = false;
 
        while (bds_to_clean && tx_frm_cnt < ENETC_DEFAULT_TX_WORK) {
                struct xdp_frame *xdp_frame = enetc_tx_swbd_get_xdp_frame(tx_swbd);
                        txbd = ENETC_TXBD(*tx_ring, i);
 
                        if (txbd->flags & ENETC_TXBD_FLAGS_W &&
-                           tx_swbd->do_tstamp) {
+                           tx_swbd->do_twostep_tstamp) {
                                enetc_get_tx_tstamp(&priv->si->hw, txbd,
                                                    &tstamp);
-                               do_tstamp = true;
+                               do_twostep_tstamp = true;
                        }
                }
 
                        xdp_return_frame(xdp_frame);
                        tx_swbd->xdp_frame = NULL;
                } else if (skb) {
-                       if (unlikely(do_tstamp)) {
+                       if (unlikely(tx_swbd->skb->cb[0] &
+                                    ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
+                               /* Start work to release lock for next one-step
+                                * timestamping packet. And send one skb in
+                                * tx_skbs queue if has.
+                                */
+                               queue_work(system_wq, &priv->tx_onestep_tstamp);
+                       } else if (unlikely(do_twostep_tstamp)) {
                                enetc_tstamp_tx(skb, tstamp);
-                               do_tstamp = false;
+                               do_twostep_tstamp = false;
                        }
                        napi_consume_skb(skb, napi_budget);
                        tx_swbd->skb = NULL;
        return 0;
 }
 
+static void enetc_tx_onestep_tstamp(struct work_struct *work)
+{
+       struct enetc_ndev_priv *priv;
+       struct sk_buff *skb;
+
+       priv = container_of(work, struct enetc_ndev_priv, tx_onestep_tstamp);
+
+       netif_tx_lock(priv->ndev);
+
+       clear_bit_unlock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS, &priv->flags);
+       skb = skb_dequeue(&priv->tx_skbs);
+       if (skb)
+               enetc_start_xmit(skb, priv->ndev);
+
+       netif_tx_unlock(priv->ndev);
+}
+
+static void enetc_tx_onestep_tstamp_init(struct enetc_ndev_priv *priv)
+{
+       INIT_WORK(&priv->tx_onestep_tstamp, enetc_tx_onestep_tstamp);
+       skb_queue_head_init(&priv->tx_skbs);
+}
+
 void enetc_start(struct net_device *ndev)
 {
        struct enetc_ndev_priv *priv = netdev_priv(ndev);
        if (err)
                goto err_set_queues;
 
+       enetc_tx_onestep_tstamp_init(priv);
        enetc_setup_bdrs(priv);
        enetc_start(ndev);
 
 
        switch (config.tx_type) {
        case HWTSTAMP_TX_OFF:
-               priv->active_offloads &= ~ENETC_F_TX_TSTAMP;
+               priv->active_offloads &= ~ENETC_F_TX_TSTAMP_MASK;
                break;
        case HWTSTAMP_TX_ON:
+               priv->active_offloads &= ~ENETC_F_TX_TSTAMP_MASK;
                priv->active_offloads |= ENETC_F_TX_TSTAMP;
                break;
+       case HWTSTAMP_TX_ONESTEP_SYNC:
+               priv->active_offloads &= ~ENETC_F_TX_TSTAMP_MASK;
+               priv->active_offloads |= ENETC_F_TX_ONESTEP_SYNC_TSTAMP;
+               break;
        default:
                return -ERANGE;
        }
 
        config.flags = 0;
 
-       if (priv->active_offloads & ENETC_F_TX_TSTAMP)
+       if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
+               config.tx_type = HWTSTAMP_TX_ONESTEP_SYNC;
+       else if (priv->active_offloads & ENETC_F_TX_TSTAMP)
                config.tx_type = HWTSTAMP_TX_ON;
        else
                config.tx_type = HWTSTAMP_TX_OFF;