#include "cmd.h"
 #include "port.h"
 #include "resources.h"
+#include "txheader.h"
 
 #define mlxsw_pci_write32(mlxsw_pci, reg, val) \
        iowrite32be(val, (mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg))
        mlxsw_pci_free_irq_vectors(mlxsw_pci);
 }
 
+static int mlxsw_pci_txhdr_construct(struct sk_buff *skb,
+                                    const struct mlxsw_txhdr_info *txhdr_info)
+{
+       const struct mlxsw_tx_info tx_info = txhdr_info->tx_info;
+       char *txhdr;
+
+       if (skb_cow_head(skb, MLXSW_TXHDR_LEN))
+               return -ENOMEM;
+
+       txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
+       memset(txhdr, 0, MLXSW_TXHDR_LEN);
+
+       mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
+       mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
+       mlxsw_tx_hdr_swid_set(txhdr, 0);
+
+       if (unlikely(txhdr_info->data)) {
+               u16 fid = txhdr_info->max_fid + tx_info.local_port - 1;
+
+               mlxsw_tx_hdr_rx_is_router_set(txhdr, true);
+               mlxsw_tx_hdr_fid_valid_set(txhdr, true);
+               mlxsw_tx_hdr_fid_set(txhdr, fid);
+               mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_DATA);
+       } else {
+               mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
+               mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
+               mlxsw_tx_hdr_port_mid_set(txhdr, tx_info.local_port);
+               mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
+       }
+
+       return 0;
+}
+
 static struct mlxsw_pci_queue *
 mlxsw_pci_sdq_pick(struct mlxsw_pci *mlxsw_pci,
                   const struct mlxsw_tx_info *tx_info)
        int i;
        int err;
 
+       err = mlxsw_pci_txhdr_construct(skb, txhdr_info);
+       if (err)
+               return err;
+
        if (skb_shinfo(skb)->nr_frags > MLXSW_PCI_WQE_SG_ENTRIES - 1) {
                err = skb_linearize(skb);
                if (err)
 
                               counter_index);
 }
 
-void mlxsw_sp_txhdr_construct(struct sk_buff *skb,
-                             const struct mlxsw_tx_info *tx_info)
-{
-       char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
-
-       memset(txhdr, 0, MLXSW_TXHDR_LEN);
-
-       mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
-       mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
-       mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
-       mlxsw_tx_hdr_swid_set(txhdr, 0);
-       mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
-       mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
-       mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
-}
-
-int
-mlxsw_sp_txhdr_ptp_data_construct(struct mlxsw_core *mlxsw_core,
-                                 struct mlxsw_sp_port *mlxsw_sp_port,
-                                 struct sk_buff *skb,
-                                 const struct mlxsw_tx_info *tx_info)
-{
-       char *txhdr;
-       u16 max_fid;
-       int err;
-
-       if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
-               err = -ENOMEM;
-               goto err_skb_cow_head;
-       }
-
-       if (!MLXSW_CORE_RES_VALID(mlxsw_core, FID)) {
-               err = -EIO;
-               goto err_res_valid;
-       }
-       max_fid = MLXSW_CORE_RES_GET(mlxsw_core, FID);
-
-       txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
-       memset(txhdr, 0, MLXSW_TXHDR_LEN);
-
-       mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
-       mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
-       mlxsw_tx_hdr_rx_is_router_set(txhdr, true);
-       mlxsw_tx_hdr_fid_valid_set(txhdr, true);
-       mlxsw_tx_hdr_fid_set(txhdr, max_fid + tx_info->local_port - 1);
-       mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_DATA);
-       return 0;
-
-err_res_valid:
-err_skb_cow_head:
-       this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
-       dev_kfree_skb_any(skb);
-       return err;
-}
-
 static bool mlxsw_sp_skb_requires_ts(struct sk_buff *skb)
 {
        unsigned int type;
        txhdr_info->max_fid = max_fid;
 }
 
-static void
+static struct sk_buff *
+mlxsw_sp_vlan_tag_push(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb)
+{
+       /* In some Spectrum ASICs, in order for PTP event packets to have their
+        * correction field correctly set on the egress port they must be
+        * transmitted as data packets. Such packets ingress the ASIC via the
+        * CPU port and must have a VLAN tag, as the CPU port is not configured
+        * with a PVID. Push the default VLAN (4095), which is configured as
+        * egress untagged on all the ports.
+        */
+       if (skb_vlan_tagged(skb))
+               return skb;
+
+       return vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q),
+                                        MLXSW_SP_DEFAULT_VID);
+}
+
+static struct sk_buff *
 mlxsw_sp_txhdr_preparations(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
                            struct mlxsw_txhdr_info *txhdr_info)
 {
        if (likely(!mlxsw_sp_skb_requires_ts(skb)))
-               return;
+               return skb;
 
        if (!mlxsw_sp->ptp_ops->tx_as_data)
-               return;
+               return skb;
 
        /* Special handling for PTP events that require a time stamp and cannot
         * be transmitted as regular control packets.
         */
        mlxsw_sp_txhdr_info_data_init(mlxsw_sp->core, skb, txhdr_info);
-}
-
-static int mlxsw_sp_txhdr_handle(struct mlxsw_core *mlxsw_core,
-                                struct mlxsw_sp_port *mlxsw_sp_port,
-                                struct sk_buff *skb,
-                                const struct mlxsw_tx_info *tx_info)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
-
-       /* In Spectrum-2 and Spectrum-3, PTP events that require a time stamp
-        * need special handling and cannot be transmitted as regular control
-        * packets.
-        */
-       if (unlikely(mlxsw_sp_skb_requires_ts(skb)))
-               return mlxsw_sp->ptp_ops->txhdr_construct(mlxsw_core,
-                                                         mlxsw_sp_port, skb,
-                                                         tx_info);
-
-       if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
-               this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
-               dev_kfree_skb_any(skb);
-               return -ENOMEM;
-       }
-
-       mlxsw_sp_txhdr_construct(skb, tx_info);
-       return 0;
+       return mlxsw_sp_vlan_tag_push(mlxsw_sp, skb);
 }
 
 enum mlxsw_reg_spms_state mlxsw_sp_stp_spms_state(u8 state)
                return NETDEV_TX_OK;
        }
 
-       mlxsw_sp_txhdr_preparations(mlxsw_sp, skb, &txhdr_info);
-
-       err = mlxsw_sp_txhdr_handle(mlxsw_sp->core, mlxsw_sp_port, skb,
-                                   &txhdr_info.tx_info);
-       if (err)
+       skb = mlxsw_sp_txhdr_preparations(mlxsw_sp, skb, &txhdr_info);
+       if (!skb) {
+               this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
                return NETDEV_TX_OK;
+       }
 
        /* TX header is consumed by HW on the way so we shouldn't count its
         * bytes as being sent.
        .get_stats_count = mlxsw_sp1_get_stats_count,
        .get_stats_strings = mlxsw_sp1_get_stats_strings,
        .get_stats      = mlxsw_sp1_get_stats,
-       .txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
 };
 
 static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
        .get_stats_count = mlxsw_sp2_get_stats_count,
        .get_stats_strings = mlxsw_sp2_get_stats_strings,
        .get_stats      = mlxsw_sp2_get_stats,
-       .txhdr_construct = mlxsw_sp2_ptp_txhdr_construct,
        .tx_as_data     = true,
 };
 
        .get_stats_count = mlxsw_sp2_get_stats_count,
        .get_stats_strings = mlxsw_sp2_get_stats_strings,
        .get_stats      = mlxsw_sp2_get_stats,
-       .txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
 };
 
 struct mlxsw_sp_sample_trigger_node {
        .trap_policer_fini              = mlxsw_sp_trap_policer_fini,
        .trap_policer_set               = mlxsw_sp_trap_policer_set,
        .trap_policer_counter_get       = mlxsw_sp_trap_policer_counter_get,
-       .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp1_resources_register,
        .kvd_sizes_get                  = mlxsw_sp_kvd_sizes_get,
        .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .trap_policer_fini              = mlxsw_sp_trap_policer_fini,
        .trap_policer_set               = mlxsw_sp_trap_policer_set,
        .trap_policer_counter_get       = mlxsw_sp_trap_policer_counter_get,
-       .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp2_resources_register,
        .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .trap_policer_fini              = mlxsw_sp_trap_policer_fini,
        .trap_policer_set               = mlxsw_sp_trap_policer_set,
        .trap_policer_counter_get       = mlxsw_sp_trap_policer_counter_get,
-       .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp2_resources_register,
        .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .trap_policer_fini              = mlxsw_sp_trap_policer_fini,
        .trap_policer_set               = mlxsw_sp_trap_policer_set,
        .trap_policer_counter_get       = mlxsw_sp_trap_policer_counter_get,
-       .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp2_resources_register,
        .ptp_transmitted                = mlxsw_sp_ptp_transmitted,
        .txhdr_len                      = MLXSW_TXHDR_LEN,