diff --git a/drivers/net/wireless/celeno/cl8k/ela.c b/drivers/net/wireless/celeno/cl8k/ela.c
new file mode 100644
index 000000000000..49f4d2d21054
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/ela.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2021, Celeno Communications Ltd. */
+
+#include "ela.h"
+#include "utils/file.h"
+#include "reg/reg_access.h"
+#include "reg/reg_lcu_common.h"
+#include "reg/reg_lcu_phy.h"
+
+#define CL_ELA_MODE_DFLT_ALIAS          "default"
+#define CL_ELA_MODE_DFLT_SYMB_LINK      "lcu_default.conf"
+#define CL_ELA_MODE_DFLT_OFF            "OFF"
+#define CL_ELA_LCU_CONF_TOKENS_CNT      3 /* cmd addr1 addr2 */
+#define CL_ELA_LCU_MEM_WRITE_CMD_STR    "mem_write"
+#define CL_ELA_LCU_MEM_WRITE_CMD_SZ     sizeof(CL_ELA_LCU_MEM_WRITE_CMD_STR)
+#define CL_ELA_LCU_UNKNOWN_CMD_TYPE     0
+#define CL_ELA_LCU_MEM_WRITE_CMD_TYPE   1
+#define CL_ELA_LCU_UNKNOWN_CMD_STR      "unknown"
+
+static int __must_check get_lcu_cmd_type(char *cmd)
+{
+       if (!strncmp(CL_ELA_LCU_MEM_WRITE_CMD_STR, cmd, CL_ELA_LCU_MEM_WRITE_CMD_SZ))
+               return CL_ELA_LCU_MEM_WRITE_CMD_TYPE;
+
+       return CL_ELA_LCU_UNKNOWN_CMD_TYPE;
+}
+
+static int add_lcu_cmd(struct cl_ela_db *ed, u32 type, u32 offset, u32 value)
+{
+       struct cl_lcu_cmd *cmd = NULL;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->type = type;
+       cmd->offset = offset;
+       cmd->value = value;
+
+       list_add_tail(&cmd->cmd_list, &ed->cmd_head);
+
+       return 0;
+}
+
+static void remove_lcu_cmd(struct cl_lcu_cmd *cmd)
+{
+       list_del(&cmd->cmd_list);
+       kfree(cmd);
+}
+
+static void reset_stats(struct cl_ela_db *db)
+{
+       memset(&db->stats, 0, sizeof(db->stats));
+}
+
+static int load_cmds_from_buf(struct cl_chip *chip, char *buf, size_t size)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+       char *line = buf;
+       char cmd[STR_LEN_256B];
+       u32 type = CL_ELA_LCU_UNKNOWN_CMD_TYPE;
+       u32 offset = 0;
+       u32 value = 0;
+       int tokens_cnt = 0;
+       int ret = 0;
+
+       while (line && strlen(line) && (line != (buf + size))) {
+               if ((*line == '#') || (*line == '\n')) {
+                       /* Skip comment or blank line */
+                       line = strstr(line, "\n") + 1;
+               } else if (*line) {
+                       tokens_cnt = sscanf(line, "%s %x %x\n", cmd, &offset, &value);
+                       cl_dbg_chip_trace(chip,
+                                         "tokens(%d):cmd(%s), offset(0x%X), value(0x%X)\n",
+                                         tokens_cnt, cmd, offset, value);
+
+                       type = get_lcu_cmd_type(cmd);
+                       if (type == CL_ELA_LCU_UNKNOWN_CMD_TYPE) {
+                               cl_dbg_chip_trace(chip, "Detected extra token, skipping\n");
+                               goto newline;
+                       }
+                       if (tokens_cnt != CL_ELA_LCU_CONF_TOKENS_CNT) {
+                               cl_dbg_chip_err(chip,
+                                               "Tokens count is wrong! (%d != %d)\n",
+                                               CL_ELA_LCU_CONF_TOKENS_CNT,
+                                               tokens_cnt);
+                               ret = -EBADMSG;
+                               goto exit;
+                       }
+
+                       ret = add_lcu_cmd(ed, type, offset, value);
+                       if (ret)
+                               goto exit;
+
+newline:
+                       line = strstr(line, "\n") + 1;
+               }
+       }
+
+exit:
+       ed->stats.adaptations_cnt++;
+       return ret;
+}
+
+void cl_ela_lcu_reset(struct cl_chip *chip)
+{
+       lcu_common_sw_rst_set(chip, 0x1);
+       lcu_phy_lcu_sw_rst_set(chip->cl_hw_tcv0, 0x1);
+       lcu_phy_lcu_sw_rst_set(chip->cl_hw_tcv1, 0x1);
+}
+
+void cl_ela_lcu_apply_config(struct cl_chip *chip)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+       struct cl_lcu_cmd *cmd = NULL;
+
+       if (!cl_ela_lcu_is_valid_config(chip)) {
+               cl_dbg_chip_err(chip, "Active ELA LCU config is not valid\n");
+               return;
+       }
+
+       list_for_each_entry(cmd, &ed->cmd_head, cmd_list) {
+               cl_dbg_chip_info(chip, "Writing cmd (0x%X, 0x%X)\n",
+                                cmd->offset, cmd->value);
+               cl_reg_write_chip(chip, cmd->offset, cmd->value);
+       }
+       ed->stats.applications_cnt++;
+}
+
+bool cl_ela_is_on(struct cl_chip *chip)
+{
+       return !!strncmp(CL_ELA_MODE_DFLT_OFF, chip->conf->ce_ela_mode,
+                        sizeof(chip->conf->ce_ela_mode));
+}
+
+bool cl_ela_is_default(struct cl_chip *chip)
+{
+       return !strncmp(CL_ELA_MODE_DFLT_ALIAS, chip->conf->ce_ela_mode,
+                       sizeof(chip->conf->ce_ela_mode));
+}
+
+bool cl_ela_lcu_is_valid_config(struct cl_chip *chip)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+
+       return ed->error_state == 0;
+}
+
+char *cl_ela_lcu_cmd_str(u32 type)
+{
+       if (type == CL_ELA_LCU_MEM_WRITE_CMD_TYPE)
+               return CL_ELA_LCU_MEM_WRITE_CMD_STR;
+
+       return CL_ELA_LCU_UNKNOWN_CMD_STR;
+}
+
+char *cl_ela_lcu_config_name(struct cl_chip *chip)
+{
+       if (!cl_ela_is_on(chip))
+               return CL_ELA_MODE_DFLT_OFF;
+
+       if (cl_ela_is_default(chip))
+               return CL_ELA_MODE_DFLT_SYMB_LINK;
+
+       return chip->conf->ce_ela_mode;
+}
+
+int cl_ela_lcu_config_read(struct cl_chip *chip)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+       char filename[CL_FILENAME_MAX] = {0};
+       size_t size = 0;
+       int ret = 0;
+
+       if (!cl_ela_is_on(chip)) {
+               ret = -EOPNOTSUPP;
+               goto exit;
+       }
+
+       reset_stats(ed);
+
+       snprintf(filename, sizeof(filename), "%s", cl_ela_lcu_config_name(chip));
+       size = cl_file_open_and_read(chip, filename, &ed->raw_lcu_config);
+       if (!ed->raw_lcu_config) {
+               ret = -ENODATA;
+               goto exit;
+       }
+
+       ret = load_cmds_from_buf(chip, ed->raw_lcu_config, size);
+exit:
+       ed->error_state = ret;
+       return ret;
+}
+
+int cl_ela_init(struct cl_chip *chip)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+       int ret = 0;
+
+       if (!cl_ela_is_on(chip))
+               return 0;
+
+       INIT_LIST_HEAD(&ed->cmd_head);
+
+       ret = cl_ela_lcu_config_read(chip);
+       if (ret == 0) {
+               cl_ela_lcu_reset(chip);
+               cl_ela_lcu_apply_config(chip);
+       }
+
+       return ret;
+}
+
+void cl_ela_deinit(struct cl_chip *chip)
+{
+       struct cl_ela_db *ed = &chip->ela_db;
+       struct cl_lcu_cmd *cmd = NULL, *cmd_tmp = NULL;
+
+       if (!cl_ela_is_on(chip))
+               return;
+
+       kfree(ed->raw_lcu_config);
+       ed->raw_lcu_config = NULL;
+
+       list_for_each_entry_safe(cmd, cmd_tmp, &ed->cmd_head, cmd_list)
+               remove_lcu_cmd(cmd);
+}
