diff --git a/drivers/net/wireless/celeno/cl8k/vendor_cmd.c b/drivers/net/wireless/celeno/cl8k/vendor_cmd.c
new file mode 100644
index 000000000000..812eebe72956
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/vendor_cmd.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/version.h>
+#include "vendor_cmd.h"
+#include "calib.h"
+#include "e2p.h"
+#include "ate.h"
+#include "cecli.h"
+#include "utils/utils.h"
+
+static int vendor_reply(struct wiphy *wiphy, void *data, u16 len)
+{
+       /* Utility function to send reply message */
+       struct sk_buff *msg = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       struct cl_msg_data *msg_data = (struct cl_msg_data *)data;
+
+       if (!msg)
+               return -ENOMEM;
+
+       if (data) {
+               if (nla_put(msg, CL_VENDOR_ATTR_REPLY, len, (void *)msg_data) < 0) {
+                       kfree_skb(msg);
+                       return -ENOBUFS;
+               }
+       }
+
+       /* Start timer if we waiting for more msg's from userspace */
+       if (msg_data->more_data)
+               cl_timer_enable(&cl_hw->vendor_timer);
+
+       return cfg80211_vendor_cmd_reply(msg);
+}
+
+static int _cl_vendor_reply(struct cl_hw *cl_hw, void *data, u16 len, bool process_data)
+{
+       struct cl_vendor_msg *vendor_msg = &cl_hw->vendor_msg;
+       struct cl_msg_data *msg_data = NULL;
+       int ret = 0;
+       u16 curr_size;
+
+       if (!data || len == 0)
+               return -EIO;
+
+       msg_data = kzalloc(sizeof(*msg_data), GFP_KERNEL);
+       if (!msg_data)
+               return -ENOMEM;
+
+       /* Messages 2..N */
+       if (process_data) {
+               curr_size = vendor_msg->len - vendor_msg->offset;
+               if (curr_size <= MSG_SIZE) {
+                       vendor_msg->in_process = false;
+               } else {
+                       curr_size = MSG_SIZE;
+                       msg_data->more_data = 1;
+               }
+
+               memcpy(msg_data->data, vendor_msg->buf + vendor_msg->offset, curr_size);
+               vendor_msg->offset += curr_size;
+               ret = vendor_reply(cl_hw->hw->wiphy, (void *)msg_data, sizeof(*msg_data));
+
+               /* Last msg */
+               if (!vendor_msg->in_process)
+                       memset(vendor_msg, 0, sizeof(*vendor_msg));
+
+               goto out;
+       }
+
+       /* Single message */
+       if (len < MSG_SIZE) {
+               memcpy(msg_data->data, data, len);
+               ret = vendor_reply(cl_hw->hw->wiphy, (void *)msg_data, sizeof(*msg_data));
+               goto out;
+       } else if (vendor_msg->in_process) {
+               goto out;
+       }
+
+       /* First message */
+       msg_data->more_data = 1;
+
+       /* Allocate buffer in driver */
+       vendor_msg->buf = data;
+
+       vendor_msg->in_process = true;
+       vendor_msg->len = len;
+
+       memcpy(msg_data->data, data, MSG_SIZE);
+       vendor_msg->offset += MSG_SIZE;
+       ret = vendor_reply(cl_hw->hw->wiphy, (void *)msg_data, sizeof(*msg_data));
+
+out:
+       kfree(msg_data);
+       return ret;
+}
+
+static int cl_vendor_cecli_handler(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  const void *data, int len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u8 cecli_cmd_id = *(u8 *)data;
+       void *real_data = (u8 *)(data + 1);
+
+       len--;
+
+       cl_timer_disable_sync(&cl_hw->vendor_timer);
+       if (cl_hw->vendor_msg.in_process &&
+           cecli_cmd_id == CL_CECLI_MORE_DATA)
+               return _cl_vendor_reply(cl_hw, real_data, len, true);
+
+       switch (cecli_cmd_id) {
+       case CL_CECLI_AGC_PARAMS:
+               return cl_cecli_agc_params(wiphy, wdev, real_data, len);
+       case CL_CECLI_BF:
+               return cl_cecli_bf(wiphy, wdev, real_data, len);
+       case CL_CECLI_CALIB:
+               return cl_cecli_calib(wiphy, wdev, real_data, len);
+       case CL_CECLI_CCA:
+               return cl_cecli_cca(wiphy, wdev, real_data, len);
+       case CL_CECLI_CHIP:
+               return cl_cecli_chip(wiphy, wdev, real_data, len);
+       case CL_CECLI_CONFIG:
+               return cl_cecli_config(wiphy, wdev, real_data, len);
+       case CL_CECLI_DEBUG:
+               return cl_cecli_debug(wiphy, wdev, real_data, len);
+       case CL_CECLI_DFS:
+               return cl_cecli_dfs(wiphy, wdev, real_data, len);
+       case CL_CECLI_EDCA:
+               return cl_cecli_edca(wiphy, wdev, real_data, len);
+       case CL_CECLI_FW:
+               return cl_cecli_fw(wiphy, wdev, real_data, len);
+       case CL_CECLI_MOTION:
+               return cl_cecli_motion(wiphy, wdev, real_data, len);
+       case CL_CECLI_NOISE:
+               return cl_cecli_noise(wiphy, wdev, real_data, len);
+       case CL_CECLI_OMI:
+               return cl_cecli_omi(wiphy, wdev, real_data, len);
+       case CL_CECLI_POWER:
+               return cl_cecli_power(wiphy, wdev, real_data, len);
+       case CL_CECLI_QOS:
+               return cl_cecli_qos(wiphy, wdev, real_data, len);
+       case CL_CECLI_RADIO:
+               return cl_cecli_radio(wiphy, wdev, real_data, len);
+       case CL_CECLI_REG:
+               return cl_cecli_reg(wiphy, wdev, real_data, len);
+       case CL_CECLI_SOUNDING:
+               return cl_cecli_sounding(wiphy, wdev, real_data, len);
+       case CL_CECLI_STATS:
+               return cl_cecli_stats(wiphy, wdev, real_data, len);
+       case CL_CECLI_TCV:
+               return cl_cecli_tcv(wiphy, wdev, real_data, len);
+       case CL_CECLI_TEMP:
+               return cl_cecli_temp(wiphy, wdev, real_data, len);
+       case CL_CECLI_TRAFFIC:
+               return cl_cecli_traffic(wiphy, wdev, real_data, len);
+       case CL_CECLI_TWT:
+               return cl_cecli_twt(wiphy, wdev, real_data, len);
+       case CL_CECLI_TXQ:
+               return cl_cecli_txq(wiphy, wdev, real_data, len);
+       case CL_CECLI_VERSION:
+               return cl_cecli_version(wiphy, wdev, real_data, len);
+       case CL_CECLI_VNS:
+               return cl_cecli_vns(wiphy, wdev, real_data, len);
+       case CL_CECLI_WRS:
+               return cl_cecli_wrs(wiphy, wdev, real_data, len);
+       default:
+               return cl_cecli_help(wiphy, wdev, real_data, len);
+       }
+
+       return 0;
+}
+
+static int cl_vendor_e2p_handler(struct wiphy *wiphy,
+                                struct wireless_dev *wdev,
+                                const void *data, int len)
+{
+       u8 e2p_cmd_id = *(u8 *)data;
+       void *real_data = (u8 *)(data + 1);
+
+       len--;
+
+       switch (e2p_cmd_id) {
+       case CL_E2P_GET_ADDR:
+       case CL_E2P_GET_MAC:
+       case CL_E2P_GET_SERIAL_NUMBER:
+       case CL_E2P_GET_PWR_TABLE_ID:
+       case CL_E2P_GET_FREQ_OFFSET:
+       case CL_E2P_GET_WIRING_ID:
+       case CL_E2P_GET_FEM_LUT:
+       case CL_E2P_GET_PLATFORM_ID:
+       case CL_E2P_GET_HEXDUMP:
+       case CL_E2P_GET_TABLE:
+               return cl_e2p_get_addr(wiphy, wdev, real_data, len);
+       case CL_E2P_GET_CALIB:
+               return cl_calib_get(wiphy, wdev, real_data, len);
+       case CL_E2P_SET_ADDR:
+       case CL_E2P_SET_MAC:
+       case CL_E2P_SET_SERIAL_NUMBER:
+       case CL_E2P_SET_PWR_TABLE_ID:
+       case CL_E2P_SET_FREQ_OFFSET:
+       case CL_E2P_SET_FEM_LUT:
+       case CL_E2P_SET_PLATFORM_ID:
+               return cl_e2p_set_addr(wiphy, wdev, real_data, len);
+       case CL_E2P_SET_WIRING_ID:
+               return cl_e2p_set_wiring_id(wiphy, wdev, real_data, len);
+       case CL_E2P_SET_CALIB:
+               return cl_calib_set(wiphy, wdev, real_data, len);
+       default:
+               return cl_e2p_help(wiphy, wdev, real_data, len);
+       }
+
+       return 0;
+}
+
+static int cl_vendor_ate_handler(struct wiphy *wiphy,
+                                struct wireless_dev *wdev,
+                                const void *data, int len)
+{
+       u8 ate_cmd_id = *(u8 *)data;
+       void *real_data = (u8 *)(data + 1);
+
+       len--;
+
+       switch (ate_cmd_id) {
+       case CL_ATE_RESET:
+               return cl_ate_reset(wiphy, wdev, real_data, len);
+       case CL_ATE_MODE:
+               return cl_ate_mode(wiphy, wdev, real_data, len);
+       case CL_ATE_BW:
+               return cl_ate_bw(wiphy, wdev, real_data, len);
+       case CL_ATE_MCS:
+               return cl_ate_mcs(wiphy, wdev, real_data, len);
+       case CL_ATE_NSS:
+               return cl_ate_nss(wiphy, wdev, real_data, len);
+       case CL_ATE_GI:
+               return cl_ate_gi(wiphy, wdev, real_data, len);
+       case CL_ATE_LTF:
+               return cl_ate_ltf(wiphy, wdev, real_data, len);
+       case CL_ATE_LDPC:
+               return cl_ate_ldpc(wiphy, wdev, real_data, len);
+       case CL_ATE_CHANNEL:
+               return cl_ate_channel(wiphy, wdev, real_data, len);
+       case CL_ATE_ANT:
+               return cl_ate_ant(wiphy, wdev, real_data, len);
+       case CL_ATE_MULTI_ANT:
+               return cl_ate_multi_ant(wiphy, wdev, real_data, len);
+       case CL_ATE_PACKET_LEN:
+               return cl_ate_packet_len(wiphy, wdev, real_data, len);
+       case CL_ATE_VECTOR_RESET:
+               return cl_ate_vector_reset(wiphy, wdev, real_data, len);
+       case CL_ATE_VECTOR:
+               return cl_ate_vector(wiphy, wdev, real_data, len);
+       case CL_ATE_FREQ_OFFSET:
+               return cl_ate_freq_offset(wiphy, wdev, real_data, len);
+       case CL_ATE_STAT_RESET:
+               return cl_ate_stat_reset(wiphy, wdev, real_data, len);
+       case CL_ATE_STAT:
+               return cl_ate_stat(wiphy, wdev, real_data, len);
+       case CL_ATE_POWER:
+               return cl_ate_power(wiphy, wdev, real_data, len);
+       case CL_ATE_POWER_OFFSET:
+               return cl_ate_power_offset(wiphy, wdev, real_data, len);
+       case CL_ATE_TX_START:
+               return cl_ate_tx_start(wiphy, wdev, real_data, len);
+       case CL_ATE_TX_CONTINUOUS:
+               return cl_ate_tx_continuous(wiphy, wdev, real_data, len);
+       case CL_ATE_STOP:
+               return cl_ate_stop(wiphy, wdev, real_data, len);
+       default:
+               return cl_ate_help(wiphy, wdev, real_data, len);
+       }
+
+       return 0;
+}
+
+static int cl_vendor_help_handler(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev,
+                                 const void *data, int len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       char ret_buf[] = {
+               "usage:\n"
+               "cecli - Celeno driver related\n"
+               "e2p   - Celeno eeprom related\n"
+               "ATE   - Celeno production related\n"
+       };
+
+       return cl_vendor_reply(cl_hw, ret_buf, strlen(ret_buf));
+}
+
+static void cl_vendor_handle_timeout(unsigned long data)
+{
+       struct cl_hw *cl_hw = (struct cl_hw *)data;
+       struct cl_vendor_msg *vendor_msg = &cl_hw->vendor_msg;
+
+       memset(vendor_msg, 0, sizeof(*vendor_msg));
+       pr_warn("cl_vendor timer expired!\n");
+}
+
+int cl_vendor_reply(struct cl_hw *cl_hw, void *data, u16 len)
+{
+       return _cl_vendor_reply(cl_hw, data, len, false);
+}
+
+void cl_vendor_timer_init(struct cl_hw *cl_hw)
+{
+       /* Init vendor_cmd timer */
+       cl_timer_init(&cl_hw->vendor_timer, cl_vendor_handle_timeout,
+                     (unsigned long)cl_hw, VENDOR_CMD_TIMER_PERIOD_MS, false);
+}
+
+void cl_vendor_timer_close(struct cl_hw *cl_hw)
+{
+       cl_timer_disable_sync(&cl_hw->vendor_timer);
+}
+
+/* Vendor specific commands */
+const struct wiphy_vendor_command cl_vendor_cmds[] = {
+       {
+               .info.vendor_id = CELENO_OUI,
+               .info.subcmd = CL_VNDR_CMDS_CECLI,
+               .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                       WIPHY_VENDOR_CMD_NEED_RUNNING |
+                       WIPHY_VENDOR_CMD_NEED_NETDEV,
+               .policy = VENDOR_CMD_RAW_DATA,
+               .doit = cl_vendor_cecli_handler
+       },
+       {
+               .info.vendor_id = CELENO_OUI,
+               .info.subcmd = CL_VNDR_CMDS_E2P,
+               .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                       WIPHY_VENDOR_CMD_NEED_RUNNING |
+                       WIPHY_VENDOR_CMD_NEED_NETDEV,
+               .policy = VENDOR_CMD_RAW_DATA,
+               .doit = cl_vendor_e2p_handler
+       },
+       {
+               .info.vendor_id = CELENO_OUI,
+               .info.subcmd = CL_VNDR_CMDS_ATE,
+               .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                       WIPHY_VENDOR_CMD_NEED_RUNNING |
+                       WIPHY_VENDOR_CMD_NEED_NETDEV,
+               .policy = VENDOR_CMD_RAW_DATA,
+               .doit = cl_vendor_ate_handler
+       },
+       {
+               .info.vendor_id = CELENO_OUI,
+               .info.subcmd = CL_VNDR_CMDS_HELP,
+               .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                       WIPHY_VENDOR_CMD_NEED_RUNNING |
+                       WIPHY_VENDOR_CMD_NEED_NETDEV,
+               .policy = VENDOR_CMD_RAW_DATA,
+               .doit = cl_vendor_help_handler
+       },
+};
+
+/* Vendor specific events */
+const struct nl80211_vendor_cmd_info cl_vendor_events[] = {
+       {
+               .vendor_id = CELENO_OUI,
+               .subcmd = CL_VENDOR_EVENT_ASYNC,
+       }
+};
+
+void cl_vendor_cmds_init(struct wiphy *wiphy)
+{
+       /* Set celeno vendor commands used by nl80211 */
+       wiphy->vendor_commands = cl_vendor_cmds;
+       wiphy->n_vendor_commands = ARRAY_SIZE(cl_vendor_cmds);
+       wiphy->vendor_events = cl_vendor_events;
+       wiphy->n_vendor_events = ARRAY_SIZE(cl_vendor_events);
+}
+
