From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/ela.c | 230 +++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/ela.c diff --git a/drivers/net/wireless/celeno/cl8k/ela.c b/drivers/net/wireless/celeno/cl8k/ela.c new file mode 100644 index 000000000000..c2419b11b5c0 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/ela.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2021, Celeno Communications Ltd. */ + +#include "reg/reg_access.h" +#include "reg/reg_defs.h" +#include "utils.h" +#include "ela.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); + + if (chip->cl_hw_tcv0) + lcu_phy_lcu_sw_rst_set(chip->cl_hw_tcv0, 0x1); + + if (chip->cl_hw_tcv1) + 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; + unsigned long flags; + + if (!cl_ela_lcu_is_valid_config(chip)) { + cl_dbg_chip_err(chip, "Active ELA LCU config is not valid\n"); + return; + } + + /* Extra safety to avoid local CPU interference during LCU reconfiguration */ + local_irq_save(flags); + 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); + if (!chip->cl_hw_tcv1 && cl_reg_is_phy_tcv1(cmd->offset)) { + ed->stats.tcv1_skips_cnt++; + continue; + } else if (!chip->cl_hw_tcv0 && cl_reg_is_phy_tcv0(cmd->offset)) { + ed->stats.tcv0_skips_cnt++; + continue; + } + cl_reg_write_chip(chip, cmd->offset, cmd->value); + } + local_irq_restore(flags); + 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_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; + + INIT_LIST_HEAD(&ed->cmd_head); + + if (!cl_ela_is_on(chip)) + return 0; + + 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; + + 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); +} -- 2.36.1