diff mbox series

[RFC,v2,27/96] cl8k: add ela.c

Message ID 20220524113502.1094459-28-viktor.barna@celeno.com (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna May 24, 2022, 11:33 a.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/ela.c | 230 +++++++++++++++++++++++++
 1 file changed, 230 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/ela.c
diff mbox series

Patch

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);
+}