break;
        }
 
-       if (ret)
+       if (ret || h->pdev->revision >= 0x21)
                return ret;
 
        if (en) {
 
 static void hns3_lp_setup_skb(struct sk_buff *skb)
 {
+#define        HNS3_NIC_LB_DST_MAC_ADDR        0x1f
+
        struct net_device *ndev = skb->dev;
+       struct hnae3_handle *handle;
        unsigned char *packet;
        struct ethhdr *ethh;
        unsigned int i;
         * before the packet reaches mac or serdes, which will defect
         * the purpose of mac or serdes selftest.
         */
-       ethh->h_dest[5] += 0x1f;
+       handle = hns3_get_handle(ndev);
+       if (handle->pdev->revision == 0x20)
+               ethh->h_dest[5] += HNS3_NIC_LB_DST_MAC_ADDR;
        eth_zero_addr(ethh->h_source);
        ethh->h_proto = htons(ETH_P_ARP);
        skb_reset_mac_header(skb);
 
        HCLGE_OPC_MAC_ETHTYPE_ADD           = 0x1010,
        HCLGE_OPC_MAC_ETHTYPE_REMOVE    = 0x1011,
 
+       /* MAC VLAN commands */
+       HCLGE_OPC_MAC_VLAN_SWITCH_PARAM = 0x1033,
+
        /* VLAN commands */
        HCLGE_OPC_VLAN_FILTER_CTRL          = 0x1100,
        HCLGE_OPC_VLAN_FILTER_PF_CFG    = 0x1101,
        u8  vf_bitmap[16];
 };
 
+#define HCLGE_SWITCH_ANTI_SPOOF_B      0U
+#define HCLGE_SWITCH_ALW_LPBK_B                1U
+#define HCLGE_SWITCH_ALW_LCL_LPBK_B    2U
+#define HCLGE_SWITCH_ALW_DST_OVRD_B    3U
+#define HCLGE_SWITCH_NO_MASK           0x0
+#define HCLGE_SWITCH_ANTI_SPOOF_MASK   0xFE
+#define HCLGE_SWITCH_ALW_LPBK_MASK     0xFD
+#define HCLGE_SWITCH_ALW_LCL_LPBK_MASK 0xFB
+#define HCLGE_SWITCH_LW_DST_OVRD_MASK  0xF7
+
+struct hclge_mac_vlan_switch_cmd {
+       u8 roce_sel;
+       u8 rsv1[3];
+       __le32 func_id;
+       u8 switch_param;
+       u8 rsv2[3];
+       u8 param_mask;
+       u8 rsv3[11];
+};
+
+enum hclge_mac_vlan_cfg_sel {
+       HCLGE_MAC_VLAN_NIC_SEL = 0,
+       HCLGE_MAC_VLAN_ROCE_SEL,
+};
+
 #define HCLGE_ACCEPT_TAG1_B            0
 #define HCLGE_ACCEPT_UNTAG1_B          1
 #define HCLGE_PORT_INS_TAG1_EN_B       2
 
                        "mac enable fail, ret =%d.\n", ret);
 }
 
+static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid,
+                                    u8 switch_param, u8 param_mask)
+{
+       struct hclge_mac_vlan_switch_cmd *req;
+       struct hclge_desc desc;
+       u32 func_id;
+       int ret;
+
+       func_id = hclge_get_port_number(HOST_PORT, 0, vfid, 0);
+       req = (struct hclge_mac_vlan_switch_cmd *)desc.data;
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_SWITCH_PARAM,
+                                  false);
+       req->roce_sel = HCLGE_MAC_VLAN_NIC_SEL;
+       req->func_id = cpu_to_le32(func_id);
+       req->switch_param = switch_param;
+       req->param_mask = param_mask;
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "set mac vlan switch parameter fail, ret = %d\n", ret);
+       return ret;
+}
+
 static void hclge_phy_link_status_wait(struct hclge_dev *hdev,
                                       int link_ret)
 {
        struct hclge_dev *hdev = vport->back;
        int i, ret;
 
+       /* Loopback can be enabled in three places: SSU, MAC, and serdes. By
+        * default, SSU loopback is enabled, so if the SMAC and the DMAC are
+        * the same, the packets are looped back in the SSU. If SSU loopback
+        * is disabled, packets can reach MAC even if SMAC is the same as DMAC.
+        */
+       if (hdev->pdev->revision >= 0x21) {
+               u8 switch_param = en ? 0 : BIT(HCLGE_SWITCH_ALW_LPBK_B);
+
+               ret = hclge_config_switch_param(hdev, PF_VPORT_ID, switch_param,
+                                               HCLGE_SWITCH_ALW_LPBK_MASK);
+               if (ret)
+                       return ret;
+       }
+
        switch (loop_mode) {
        case HNAE3_LOOP_APP:
                ret = hclge_set_app_loopback(hdev, en);