diff --git a/drivers/net/wireless/celeno/cl8k/edca.c b/drivers/net/wireless/celeno/cl8k/edca.c
new file mode 100644
index 000000000000..d17a7bd674c5
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/edca.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "edca.h"
+#include "sta.h"
+#include "vendor_cmd.h"
+#include "fw/msg_tx.h"
+#include "utils/utils.h"
+
+static u8 conv_to_fw_ac[EDCA_AC_MAX] = {
+       [EDCA_AC_BE] = AC_BE,
+       [EDCA_AC_BK] = AC_BK,
+       [EDCA_AC_VI] = AC_VI,
+       [EDCA_AC_VO] = AC_VO
+};
+
+static const char *edca_ac_str[EDCA_AC_MAX] = {
+       [EDCA_AC_BE] = "BE",
+       [EDCA_AC_BK] = "BK",
+       [EDCA_AC_VI] = "VI",
+       [EDCA_AC_VO] = "VO"
+};
+
+static int cl_edca_print(struct cl_hw *cl_hw)
+{
+       u8 ac = 0;
+       struct edca_params *params;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int err = 0;
+       int len = 0;
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "---------------------------------------\n"
+                   "| ac | aifsn | cw_min | cw_max | txop |\n"
+                   "|----+-------+--------+--------+------|\n");
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               params = &cl_hw->edca_db.hw_params[ac];
+               cl_snprintf(&buf, &len, &buf_size,
+                           "| %s | %5u | %6u | %6u | %4u |\n",
+                           edca_ac_str[ac], params->aifsn, params->cw_min,
+                           params->cw_max, params->txop);
+       }
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "---------------------------------------\n\n");
+
+       err = cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+
+       return err;
+}
+
+static void cl_edca_set_conf(struct cl_hw *cl_hw, u8 ac)
+{
+       struct edca_params params = {
+               .aifsn = cl_hw->conf->ce_wmm_aifsn[ac],
+               .cw_min = cl_hw->conf->ce_wmm_cwmin[ac],
+               .cw_max = cl_hw->conf->ce_wmm_cwmax[ac],
+               .txop = cl_hw->conf->ce_wmm_txop[ac]
+       };
+
+       cl_edca_set(cl_hw, ac, &params, NULL);
+}
+
+static void cl_edca_ac_set(struct cl_hw *cl_hw, u8 ac, u8 aifsn, u8 cw_min, u8 cw_max, u16 txop)
+{
+       pr_debug("ac = %u, aifsn = %u, cw_min = %u, cw_max = %u, txop = %u\n",
+                ac, aifsn, cw_min, cw_max, txop);
+
+       cl_hw->conf->ce_wmm_aifsn[ac] = aifsn;
+       cl_hw->conf->ce_wmm_cwmin[ac] = cw_min;
+       cl_hw->conf->ce_wmm_cwmax[ac] = cw_max;
+       cl_hw->conf->ce_wmm_txop[ac] = txop;
+
+       cl_edca_set_conf(cl_hw, ac);
+}
+
+static void cl_edca_aifsn_set(struct cl_hw *cl_hw, s32 aifsn[AC_MAX])
+{
+       u8 ac = 0;
+
+       pr_debug("Set aifsn: BE = %d, BK = %d, VI = %d, VO = %d\n",
+                aifsn[0], aifsn[1], aifsn[2], aifsn[3]);
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               cl_hw->conf->ce_wmm_aifsn[ac] = (u8)aifsn[ac];
+               cl_edca_set_conf(cl_hw, ac);
+       }
+}
+
+static void cl_edca_cwmin_set(struct cl_hw *cl_hw, s32 cw_min[AC_MAX])
+{
+       u8 ac = 0;
+
+       pr_debug("Set cw_min: BE = %d, BK = %d, VI = %d, VO = %d\n",
+                cw_min[0], cw_min[1], cw_min[2], cw_min[3]);
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               cl_hw->conf->ce_wmm_cwmin[ac] = (u8)cw_min[ac];
+               cl_edca_set_conf(cl_hw, ac);
+       }
+}
+
+static void cl_edca_cwmax_set(struct cl_hw *cl_hw, s32 cw_max[AC_MAX])
+{
+       u8 ac = 0;
+
+       pr_debug("Set cw_max: BE = %d, BK = %d, VI = %d, VO = %d\n",
+                cw_max[0], cw_max[1], cw_max[2], cw_max[3]);
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               cl_hw->conf->ce_wmm_cwmax[ac] = (u8)cw_max[ac];
+               cl_edca_set_conf(cl_hw, ac);
+       }
+}
+
+static void cl_edca_txop_set(struct cl_hw *cl_hw, s32 txop[AC_MAX])
+{
+       u8 ac = 0;
+
+       pr_debug("Set txop: BE = %d, BK = %d, VI = %d, VO = %d\n",
+                txop[0], txop[1], txop[2], txop[3]);
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               cl_hw->conf->ce_wmm_txop[ac] = (u16)txop[ac];
+               cl_edca_set_conf(cl_hw, ac);
+       }
+}
+
+static int cl_edca_cli_help(struct cl_hw *cl_hw)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       snprintf(buf, PAGE_SIZE,
+                "edca usage:\n"
+                "-a : Print current configuration\n"
+                "-b : Set per AC [0-BE,1-BK,2-VI,3-VO].[aifsn].[cw_min]."
+                       "[cw_max].[txop]\n"
+                "-c : Set aifsn [BE].[BK].[VI].[VO]\n"
+                "-d : Set cw_min [BE].[BK].[VI].[VO]\n"
+                "-e : Set cw_max [BE].[BK].[VI].[VO]\n"
+                "-f : Set txop  [BE].[BK].[VI].[VO]\n");
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+void cl_edca_hw_conf(struct cl_hw *cl_hw)
+{
+       u8 ac = 0;
+       struct cl_tcv_conf *conf = cl_hw->conf;
+
+       for (ac = 0; ac < AC_MAX; ac++) {
+               struct edca_params params = {
+                       .aifsn = conf->ce_wmm_aifsn[ac],
+                       .cw_min = conf->ce_wmm_cwmin[ac],
+                       .cw_max = conf->ce_wmm_cwmax[ac],
+                       .txop = conf->ce_wmm_txop[ac]
+               };
+
+               cl_edca_set(cl_hw, ac, &params, NULL);
+       }
+}
+
+void cl_edca_set(struct cl_hw *cl_hw, u8 ac, struct edca_params *params,
+                struct ieee80211_he_mu_edca_param_ac_rec *mu_edca)
+{
+       u32 edca_reg_val = 0;
+
+       if (ac >= AC_MAX) {
+               pr_err("%s: Invalid AC index\n", __func__);
+               return;
+       }
+
+       edca_reg_val  = (u32)(params->aifsn);
+       edca_reg_val |= (u32)(params->cw_min << 4);
+       edca_reg_val |= (u32)(params->cw_max << 8);
+       edca_reg_val |= (u32)(params->txop << 12);
+
+       memcpy(&cl_hw->edca_db.hw_params[ac], params, sizeof(struct edca_params));
+
+       cl_msg_tx_set_edca(cl_hw, conv_to_fw_ac[ac], edca_reg_val, mu_edca);
+
+       cl_dbg_trace(cl_hw, "EDCA-%s: aifsn=%u, cw_min=%u, cw_max=%u, txop=%u\n",
+                    edca_ac_str[ac], params->aifsn, params->cw_min, params->cw_max, params->txop);
+}
+
+void cl_edca_restore_conf(struct cl_hw *cl_hw, u8 ac)
+{
+       cl_edca_set_conf(cl_hw, ac);
+}
+
+void cl_edca_recovery(struct cl_hw *cl_hw)
+{
+       u8 ac;
+
+       for (ac = 0; ac < AC_MAX; ac++)
+               cl_edca_set(cl_hw, ac, &cl_hw->edca_db.hw_params[ac], NULL);
+}
+
+int cl_edca_cli(struct cl_hw *cl_hw, struct cli_params *cli_params)
+{
+       u8 ac, aifsn, cw_min, cw_max;
+       u16 txop;
+
+       switch (cli_params->option) {
+       case 'a':
+               return cl_edca_print(cl_hw);
+       case 'b':
+               if (cli_params->num_params != 5)
+                       goto err_num_of_arg;
+
+               ac = (u8)cli_params->params[0];
+               aifsn = (u8)cli_params->params[1];
+               cw_min = (u8)cli_params->params[2];
+               cw_max = (u8)cli_params->params[3];
+               txop = (u16)cli_params->params[4];
+
+               cl_edca_ac_set(cl_hw, ac, aifsn, cw_min, cw_max, txop);
+               break;
+       case 'c':
+               if (cli_params->num_params != AC_MAX)
+                       goto err_num_of_arg;
+
+               cl_edca_aifsn_set(cl_hw, cli_params->params);
+               break;
+       case 'd':
+               if (cli_params->num_params != AC_MAX)
+                       goto err_num_of_arg;
+
+               cl_edca_cwmin_set(cl_hw, cli_params->params);
+               break;
+       case 'e':
+               if (cli_params->num_params != AC_MAX)
+                       goto err_num_of_arg;
+
+               cl_edca_cwmax_set(cl_hw, cli_params->params);
+               break;
+       case 'f':
+               if (cli_params->num_params != AC_MAX)
+                       goto err_num_of_arg;
+
+               cl_edca_txop_set(cl_hw, cli_params->params);
+               break;
+       case '?':
+               return cl_edca_cli_help(cl_hw);
+       default:
+               cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n", cli_params->option);
+               break;
+       }
+
+       return 0;
+
+err_num_of_arg:
+       pr_err("wrong number of arguments\n");
+       return 0;
+}
