* SOFTWARE.
  */
 
-#include <linux/io.h>
 #include <rdma/ib_umem_odp.h>
 #include "mlx5_ib.h"
-#include <linux/jiffies.h>
 
 /*
  * Fill in a physical address list. ib_umem_num_dma_blocks() entries will be
                return 0;
        return page_size;
 }
-
-#define WR_ID_BF 0xBF
-#define WR_ID_END 0xBAD
-#define TEST_WC_NUM_WQES 255
-#define TEST_WC_POLLING_MAX_TIME_JIFFIES msecs_to_jiffies(100)
-static int post_send_nop(struct mlx5_ib_dev *dev, struct ib_qp *ibqp, u64 wr_id,
-                        bool signaled)
-{
-       struct mlx5_ib_qp *qp = to_mqp(ibqp);
-       struct mlx5_wqe_ctrl_seg *ctrl;
-       struct mlx5_bf *bf = &qp->bf;
-       __be32 mmio_wqe[16] = {};
-       unsigned long flags;
-       unsigned int idx;
-
-       if (unlikely(dev->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR))
-               return -EIO;
-
-       spin_lock_irqsave(&qp->sq.lock, flags);
-
-       idx = qp->sq.cur_post & (qp->sq.wqe_cnt - 1);
-       ctrl = mlx5_frag_buf_get_wqe(&qp->sq.fbc, idx);
-
-       memset(ctrl, 0, sizeof(struct mlx5_wqe_ctrl_seg));
-       ctrl->fm_ce_se = signaled ? MLX5_WQE_CTRL_CQ_UPDATE : 0;
-       ctrl->opmod_idx_opcode =
-               cpu_to_be32(((u32)(qp->sq.cur_post) << 8) | MLX5_OPCODE_NOP);
-       ctrl->qpn_ds = cpu_to_be32((sizeof(struct mlx5_wqe_ctrl_seg) / 16) |
-                                  (qp->trans_qp.base.mqp.qpn << 8));
-
-       qp->sq.wrid[idx] = wr_id;
-       qp->sq.w_list[idx].opcode = MLX5_OPCODE_NOP;
-       qp->sq.wqe_head[idx] = qp->sq.head + 1;
-       qp->sq.cur_post += DIV_ROUND_UP(sizeof(struct mlx5_wqe_ctrl_seg),
-                                       MLX5_SEND_WQE_BB);
-       qp->sq.w_list[idx].next = qp->sq.cur_post;
-       qp->sq.head++;
-
-       memcpy(mmio_wqe, ctrl, sizeof(*ctrl));
-       ((struct mlx5_wqe_ctrl_seg *)&mmio_wqe)->fm_ce_se |=
-               MLX5_WQE_CTRL_CQ_UPDATE;
-
-       /* Make sure that descriptors are written before
-        * updating doorbell record and ringing the doorbell
-        */
-       wmb();
-
-       qp->db.db[MLX5_SND_DBR] = cpu_to_be32(qp->sq.cur_post);
-
-       /* Make sure doorbell record is visible to the HCA before
-        * we hit doorbell
-        */
-       wmb();
-       __iowrite64_copy(bf->bfreg->map + bf->offset, mmio_wqe,
-                        sizeof(mmio_wqe) / 8);
-
-       bf->offset ^= bf->buf_size;
-
-       spin_unlock_irqrestore(&qp->sq.lock, flags);
-
-       return 0;
-}
-
-static int test_wc_poll_cq_result(struct mlx5_ib_dev *dev, struct ib_cq *cq)
-{
-       int ret;
-       struct ib_wc wc = {};
-       unsigned long end = jiffies + TEST_WC_POLLING_MAX_TIME_JIFFIES;
-
-       do {
-               ret = ib_poll_cq(cq, 1, &wc);
-               if (ret < 0 || wc.status)
-                       return ret < 0 ? ret : -EINVAL;
-               if (ret)
-                       break;
-       } while (!time_after(jiffies, end));
-
-       if (!ret)
-               return -ETIMEDOUT;
-
-       if (wc.wr_id != WR_ID_BF)
-               ret = 0;
-
-       return ret;
-}
-
-static int test_wc_do_send(struct mlx5_ib_dev *dev, struct ib_qp *qp)
-{
-       int err, i;
-
-       for (i = 0; i < TEST_WC_NUM_WQES; i++) {
-               err = post_send_nop(dev, qp, WR_ID_BF, false);
-               if (err)
-                       return err;
-       }
-
-       return post_send_nop(dev, qp, WR_ID_END, true);
-}
-
-int mlx5_ib_test_wc(struct mlx5_ib_dev *dev)
-{
-       struct ib_cq_init_attr cq_attr = { .cqe = TEST_WC_NUM_WQES + 1 };
-       int port_type_cap = MLX5_CAP_GEN(dev->mdev, port_type);
-       struct ib_qp_init_attr qp_init_attr = {
-               .cap = { .max_send_wr = TEST_WC_NUM_WQES },
-               .qp_type = IB_QPT_UD,
-               .sq_sig_type = IB_SIGNAL_REQ_WR,
-               .create_flags = MLX5_IB_QP_CREATE_WC_TEST,
-       };
-       struct ib_qp_attr qp_attr = { .port_num = 1 };
-       struct ib_device *ibdev = &dev->ib_dev;
-       struct ib_qp *qp;
-       struct ib_cq *cq;
-       struct ib_pd *pd;
-       int ret;
-
-       if (!MLX5_CAP_GEN(dev->mdev, bf))
-               return 0;
-
-       if (!dev->mdev->roce.roce_en &&
-           port_type_cap == MLX5_CAP_PORT_TYPE_ETH) {
-               if (mlx5_core_is_pf(dev->mdev))
-                       dev->wc_support = arch_can_pci_mmap_wc();
-               return 0;
-       }
-
-       ret = mlx5_alloc_bfreg(dev->mdev, &dev->wc_bfreg, true, false);
-       if (ret)
-               goto print_err;
-
-       if (!dev->wc_bfreg.wc)
-               goto out1;
-
-       pd = ib_alloc_pd(ibdev, 0);
-       if (IS_ERR(pd)) {
-               ret = PTR_ERR(pd);
-               goto out1;
-       }
-
-       cq = ib_create_cq(ibdev, NULL, NULL, NULL, &cq_attr);
-       if (IS_ERR(cq)) {
-               ret = PTR_ERR(cq);
-               goto out2;
-       }
-
-       qp_init_attr.recv_cq = cq;
-       qp_init_attr.send_cq = cq;
-       qp = ib_create_qp(pd, &qp_init_attr);
-       if (IS_ERR(qp)) {
-               ret = PTR_ERR(qp);
-               goto out3;
-       }
-
-       qp_attr.qp_state = IB_QPS_INIT;
-       ret = ib_modify_qp(qp, &qp_attr,
-                          IB_QP_STATE | IB_QP_PORT | IB_QP_PKEY_INDEX |
-                                  IB_QP_QKEY);
-       if (ret)
-               goto out4;
-
-       qp_attr.qp_state = IB_QPS_RTR;
-       ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
-       if (ret)
-               goto out4;
-
-       qp_attr.qp_state = IB_QPS_RTS;
-       ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN);
-       if (ret)
-               goto out4;
-
-       ret = test_wc_do_send(dev, qp);
-       if (ret < 0)
-               goto out4;
-
-       ret = test_wc_poll_cq_result(dev, cq);
-       if (ret > 0) {
-               dev->wc_support = true;
-               ret = 0;
-       }
-
-out4:
-       ib_destroy_qp(qp);
-out3:
-       ib_destroy_cq(cq);
-out2:
-       ib_dealloc_pd(pd);
-out1:
-       mlx5_free_bfreg(dev->mdev, &dev->wc_bfreg);
-print_err:
-       if (ret)
-               mlx5_ib_err(
-                       dev,
-                       "Error %d while trying to test write-combining support\n",
-                       ret);
-       return ret;
-}
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+#include <linux/io.h>
+#include <linux/mlx5/transobj.h>
+#include "lib/clock.h"
+#include "mlx5_core.h"
+#include "wq.h"
+
+#define TEST_WC_NUM_WQES 255
+#define TEST_WC_LOG_CQ_SZ (order_base_2(TEST_WC_NUM_WQES))
+#define TEST_WC_SQ_LOG_WQ_SZ TEST_WC_LOG_CQ_SZ
+#define TEST_WC_POLLING_MAX_TIME_JIFFIES msecs_to_jiffies(100)
+
+struct mlx5_wc_cq {
+       /* data path - accessed per cqe */
+       struct mlx5_cqwq wq;
+
+       /* data path - accessed per napi poll */
+       struct mlx5_core_cq mcq;
+
+       /* control */
+       struct mlx5_core_dev *mdev;
+       struct mlx5_wq_ctrl wq_ctrl;
+};
+
+struct mlx5_wc_sq {
+       /* data path */
+       u16 cc;
+       u16 pc;
+
+       /* read only */
+       struct mlx5_wq_cyc wq;
+       u32 sqn;
+
+       /* control path */
+       struct mlx5_wq_ctrl wq_ctrl;
+
+       struct mlx5_wc_cq cq;
+       struct mlx5_sq_bfreg bfreg;
+};
+
+static int mlx5_wc_create_cqwq(struct mlx5_core_dev *mdev, void *cqc,
+                              struct mlx5_wc_cq *cq)
+{
+       struct mlx5_core_cq *mcq = &cq->mcq;
+       struct mlx5_wq_param param = {};
+       int err;
+       u32 i;
+
+       err = mlx5_cqwq_create(mdev, ¶m, cqc, &cq->wq, &cq->wq_ctrl);
+       if (err)
+               return err;
+
+       mcq->cqe_sz     = 64;
+       mcq->set_ci_db  = cq->wq_ctrl.db.db;
+       mcq->arm_db     = cq->wq_ctrl.db.db + 1;
+
+       for (i = 0; i < mlx5_cqwq_get_size(&cq->wq); i++) {
+               struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(&cq->wq, i);
+
+               cqe->op_own = 0xf1;
+       }
+
+       cq->mdev = mdev;
+
+       return 0;
+}
+
+static int create_wc_cq(struct mlx5_wc_cq *cq, void *cqc_data)
+{
+       u32 out[MLX5_ST_SZ_DW(create_cq_out)];
+       struct mlx5_core_dev *mdev = cq->mdev;
+       struct mlx5_core_cq *mcq = &cq->mcq;
+       int err, inlen, eqn;
+       void *in, *cqc;
+
+       err = mlx5_comp_eqn_get(mdev, 0, &eqn);
+       if (err)
+               return err;
+
+       inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
+               sizeof(u64) * cq->wq_ctrl.buf.npages;
+       in = kvzalloc(inlen, GFP_KERNEL);
+       if (!in)
+               return -ENOMEM;
+
+       cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
+
+       memcpy(cqc, cqc_data, MLX5_ST_SZ_BYTES(cqc));
+
+       mlx5_fill_page_frag_array(&cq->wq_ctrl.buf,
+                                 (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas));
+
+       MLX5_SET(cqc,   cqc, cq_period_mode, MLX5_CQ_PERIOD_MODE_START_FROM_EQE);
+       MLX5_SET(cqc,   cqc, c_eqn_or_apu_element, eqn);
+       MLX5_SET(cqc,   cqc, uar_page,      mdev->priv.uar->index);
+       MLX5_SET(cqc,   cqc, log_page_size, cq->wq_ctrl.buf.page_shift -
+                                           MLX5_ADAPTER_PAGE_SHIFT);
+       MLX5_SET64(cqc, cqc, dbr_addr,      cq->wq_ctrl.db.dma);
+
+       err = mlx5_core_create_cq(mdev, mcq, in, inlen, out, sizeof(out));
+
+       kvfree(in);
+
+       return err;
+}
+
+static int mlx5_wc_create_cq(struct mlx5_core_dev *mdev, struct mlx5_wc_cq *cq)
+{
+       void *cqc;
+       int err;
+
+       cqc = kvzalloc(MLX5_ST_SZ_BYTES(cqc), GFP_KERNEL);
+       if (!cqc)
+               return -ENOMEM;
+
+       MLX5_SET(cqc, cqc, log_cq_size, TEST_WC_LOG_CQ_SZ);
+       MLX5_SET(cqc, cqc, uar_page, mdev->priv.uar->index);
+       if (MLX5_CAP_GEN(mdev, cqe_128_always) && cache_line_size() >= 128)
+               MLX5_SET(cqc, cqc, cqe_sz, CQE_STRIDE_128_PAD);
+
+       err = mlx5_wc_create_cqwq(mdev, cqc, cq);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to create wc cq wq, err=%d\n", err);
+               goto err_create_cqwq;
+       }
+
+       err = create_wc_cq(cq, cqc);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to create wc cq, err=%d\n", err);
+               goto err_create_cq;
+       }
+
+       kvfree(cqc);
+       return 0;
+
+err_create_cq:
+       mlx5_wq_destroy(&cq->wq_ctrl);
+err_create_cqwq:
+       kvfree(cqc);
+       return err;
+}
+
+static void mlx5_wc_destroy_cq(struct mlx5_wc_cq *cq)
+{
+       mlx5_core_destroy_cq(cq->mdev, &cq->mcq);
+       mlx5_wq_destroy(&cq->wq_ctrl);
+}
+
+static int create_wc_sq(struct mlx5_core_dev *mdev, void *sqc_data,
+                       struct mlx5_wc_sq *sq)
+{
+       void *in, *sqc, *wq;
+       int inlen, err;
+       u8 ts_format;
+
+       inlen = MLX5_ST_SZ_BYTES(create_sq_in) +
+               sizeof(u64) * sq->wq_ctrl.buf.npages;
+       in = kvzalloc(inlen, GFP_KERNEL);
+       if (!in)
+               return -ENOMEM;
+
+       sqc = MLX5_ADDR_OF(create_sq_in, in, ctx);
+       wq = MLX5_ADDR_OF(sqc, sqc, wq);
+
+       memcpy(sqc, sqc_data, MLX5_ST_SZ_BYTES(sqc));
+       MLX5_SET(sqc,  sqc, cqn, sq->cq.mcq.cqn);
+
+       MLX5_SET(sqc,  sqc, state, MLX5_SQC_STATE_RST);
+       MLX5_SET(sqc,  sqc, flush_in_error_en, 1);
+
+       ts_format = mlx5_is_real_time_sq(mdev) ?
+                       MLX5_TIMESTAMP_FORMAT_REAL_TIME :
+                       MLX5_TIMESTAMP_FORMAT_FREE_RUNNING;
+       MLX5_SET(sqc, sqc, ts_format, ts_format);
+
+       MLX5_SET(wq,   wq, wq_type,       MLX5_WQ_TYPE_CYCLIC);
+       MLX5_SET(wq,   wq, uar_page,      sq->bfreg.index);
+       MLX5_SET(wq,   wq, log_wq_pg_sz,  sq->wq_ctrl.buf.page_shift -
+                                         MLX5_ADAPTER_PAGE_SHIFT);
+       MLX5_SET64(wq, wq, dbr_addr,      sq->wq_ctrl.db.dma);
+
+       mlx5_fill_page_frag_array(&sq->wq_ctrl.buf,
+                                 (__be64 *)MLX5_ADDR_OF(wq, wq, pas));
+
+       err = mlx5_core_create_sq(mdev, in, inlen, &sq->sqn);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to create wc sq, err=%d\n", err);
+               goto err_create_sq;
+       }
+
+       memset(in, 0,  MLX5_ST_SZ_BYTES(modify_sq_in));
+       MLX5_SET(modify_sq_in, in, sq_state, MLX5_SQC_STATE_RST);
+       sqc = MLX5_ADDR_OF(modify_sq_in, in, ctx);
+       MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RDY);
+
+       err = mlx5_core_modify_sq(mdev, sq->sqn, in);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to set wc sq(sqn=0x%x) ready, err=%d\n",
+                             sq->sqn, err);
+               goto err_modify_sq;
+       }
+
+       kvfree(in);
+       return 0;
+
+err_modify_sq:
+       mlx5_core_destroy_sq(mdev, sq->sqn);
+err_create_sq:
+       kvfree(in);
+       return err;
+}
+
+static int mlx5_wc_create_sq(struct mlx5_core_dev *mdev, struct mlx5_wc_sq *sq)
+{
+       struct mlx5_wq_param param = {};
+       void *sqc_data, *wq;
+       int err;
+
+       sqc_data = kvzalloc(MLX5_ST_SZ_BYTES(sqc), GFP_KERNEL);
+       if (!sqc_data)
+               return -ENOMEM;
+
+       wq = MLX5_ADDR_OF(sqc, sqc_data, wq);
+       MLX5_SET(wq, wq, log_wq_stride, ilog2(MLX5_SEND_WQE_BB));
+       MLX5_SET(wq, wq, pd, mdev->mlx5e_res.hw_objs.pdn);
+       MLX5_SET(wq, wq, log_wq_sz, TEST_WC_SQ_LOG_WQ_SZ);
+
+       err = mlx5_wq_cyc_create(mdev, ¶m, wq, &sq->wq, &sq->wq_ctrl);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to create wc sq wq, err=%d\n", err);
+               goto err_create_wq_cyc;
+       }
+
+       err = create_wc_sq(mdev, sqc_data, sq);
+       if (err)
+               goto err_create_sq;
+
+       mlx5_core_dbg(mdev, "wc sq->sqn = 0x%x created\n", sq->sqn);
+
+       kvfree(sqc_data);
+       return 0;
+
+err_create_sq:
+       mlx5_wq_destroy(&sq->wq_ctrl);
+err_create_wq_cyc:
+       kvfree(sqc_data);
+       return err;
+}
+
+static void mlx5_wc_destroy_sq(struct mlx5_wc_sq *sq)
+{
+       mlx5_core_destroy_sq(sq->cq.mdev, sq->sqn);
+       mlx5_wq_destroy(&sq->wq_ctrl);
+}
+
+static void mlx5_wc_post_nop(struct mlx5_wc_sq *sq, bool signaled)
+{
+       int buf_size = (1 << MLX5_CAP_GEN(sq->cq.mdev, log_bf_reg_size)) / 2;
+       struct mlx5_wqe_ctrl_seg *ctrl;
+       __be32 mmio_wqe[16] = {};
+       u16 pi;
+
+       pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       ctrl = mlx5_wq_cyc_get_wqe(&sq->wq, pi);
+       memset(ctrl, 0, sizeof(*ctrl));
+       ctrl->opmod_idx_opcode =
+               cpu_to_be32((sq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) | MLX5_OPCODE_NOP);
+       ctrl->qpn_ds =
+               cpu_to_be32((sq->sqn << MLX5_WQE_CTRL_QPN_SHIFT) |
+                           DIV_ROUND_UP(sizeof(struct mlx5_wqe_ctrl_seg), MLX5_SEND_WQE_DS));
+       if (signaled)
+               ctrl->fm_ce_se |= MLX5_WQE_CTRL_CQ_UPDATE;
+
+       memcpy(mmio_wqe, ctrl, sizeof(*ctrl));
+       ((struct mlx5_wqe_ctrl_seg *)&mmio_wqe)->fm_ce_se |=
+               MLX5_WQE_CTRL_CQ_UPDATE;
+
+       /* ensure wqe is visible to device before updating doorbell record */
+       dma_wmb();
+
+       sq->pc++;
+       sq->wq.db[MLX5_SND_DBR] = cpu_to_be32(sq->pc);
+
+       /* ensure doorbell record is visible to device before ringing the
+        * doorbell
+        */
+       wmb();
+
+       __iowrite64_copy(sq->bfreg.map + sq->bfreg.offset, mmio_wqe,
+                        sizeof(mmio_wqe) / 8);
+
+       sq->bfreg.offset ^= buf_size;
+}
+
+static int mlx5_wc_poll_cq(struct mlx5_wc_sq *sq)
+{
+       struct mlx5_wc_cq *cq = &sq->cq;
+       struct mlx5_cqe64 *cqe;
+
+       cqe = mlx5_cqwq_get_cqe(&cq->wq);
+       if (!cqe)
+               return -ETIMEDOUT;
+
+       /* sq->cc must be updated only after mlx5_cqwq_update_db_record(),
+        * otherwise a cq overrun may occur
+        */
+       mlx5_cqwq_pop(&cq->wq);
+
+       if (get_cqe_opcode(cqe) == MLX5_CQE_REQ) {
+               int wqe_counter = be16_to_cpu(cqe->wqe_counter);
+               struct mlx5_core_dev *mdev = cq->mdev;
+
+               if (wqe_counter == TEST_WC_NUM_WQES - 1)
+                       mdev->wc_state = MLX5_WC_STATE_UNSUPPORTED;
+               else
+                       mdev->wc_state = MLX5_WC_STATE_SUPPORTED;
+
+               mlx5_core_dbg(mdev, "wc wqe_counter = 0x%x\n", wqe_counter);
+       }
+
+       mlx5_cqwq_update_db_record(&cq->wq);
+
+       /* ensure cq space is freed before enabling more cqes */
+       wmb();
+
+       sq->cc++;
+
+       return 0;
+}
+
+static void mlx5_core_test_wc(struct mlx5_core_dev *mdev)
+{
+       unsigned long expires;
+       struct mlx5_wc_sq *sq;
+       int i, err;
+
+       if (mdev->wc_state != MLX5_WC_STATE_UNINITIALIZED)
+               return;
+
+       sq = kzalloc(sizeof(*sq), GFP_KERNEL);
+       if (!sq)
+               return;
+
+       err = mlx5_alloc_bfreg(mdev, &sq->bfreg, true, false);
+       if (err) {
+               mlx5_core_err(mdev, "Failed to alloc bfreg for wc, err=%d\n", err);
+               goto err_alloc_bfreg;
+       }
+
+       err = mlx5_wc_create_cq(mdev, &sq->cq);
+       if (err)
+               goto err_create_cq;
+
+       err = mlx5_wc_create_sq(mdev, sq);
+       if (err)
+               goto err_create_sq;
+
+       for (i = 0; i < TEST_WC_NUM_WQES - 1; i++)
+               mlx5_wc_post_nop(sq, false);
+
+       mlx5_wc_post_nop(sq, true);
+
+       expires = jiffies + TEST_WC_POLLING_MAX_TIME_JIFFIES;
+       do {
+               err = mlx5_wc_poll_cq(sq);
+               if (err)
+                       usleep_range(2, 10);
+       } while (mdev->wc_state == MLX5_WC_STATE_UNINITIALIZED &&
+                time_is_after_jiffies(expires));
+
+       mlx5_wc_destroy_sq(sq);
+
+err_create_sq:
+       mlx5_wc_destroy_cq(&sq->cq);
+err_create_cq:
+       mlx5_free_bfreg(mdev, &sq->bfreg);
+err_alloc_bfreg:
+       kfree(sq);
+}
+
+bool mlx5_wc_support_get(struct mlx5_core_dev *mdev)
+{
+       struct mlx5_core_dev *parent = NULL;
+
+       if (!MLX5_CAP_GEN(mdev, bf)) {
+               mlx5_core_dbg(mdev, "BlueFlame not supported\n");
+               goto out;
+       }
+
+       if (!MLX5_CAP_GEN(mdev, log_max_sq)) {
+               mlx5_core_dbg(mdev, "SQ not supported\n");
+               goto out;
+       }
+
+       if (mdev->wc_state != MLX5_WC_STATE_UNINITIALIZED)
+               /* No need to lock anything as we perform WC test only
+                * once for whole device and was already done.
+                */
+               goto out;
+
+       mutex_lock(&mdev->wc_state_lock);
+
+       if (mdev->wc_state != MLX5_WC_STATE_UNINITIALIZED)
+               goto unlock;
+
+#ifdef CONFIG_MLX5_SF
+       if (mlx5_core_is_sf(mdev))
+               parent = mdev->priv.parent_mdev;
+#endif
+
+       if (parent) {
+               mutex_lock(&parent->wc_state_lock);
+
+               mlx5_core_test_wc(parent);
+
+               mlx5_core_dbg(mdev, "parent set wc_state=%d\n",
+                             parent->wc_state);
+               mdev->wc_state = parent->wc_state;
+
+               mutex_unlock(&parent->wc_state_lock);
+       }
+
+       mlx5_core_test_wc(mdev);
+
+unlock:
+       mutex_unlock(&mdev->wc_state_lock);
+out:
+       mlx5_core_dbg(mdev, "wc_state=%d\n", mdev->wc_state);
+
+       return mdev->wc_state == MLX5_WC_STATE_SUPPORTED;
+}
+EXPORT_SYMBOL(mlx5_wc_support_get);