new file mode 100644
@@ -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, ¶ms, 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, ¶ms, 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;
+}