diff mbox series

[RFC,v2,66/96] cl8k: add rfic.c

Message ID 20220524113502.1094459-67-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna May 24, 2022, 11:34 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/rfic.c | 232 ++++++++++++++++++++++++
 1 file changed, 232 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/rfic.c
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/rfic.c b/drivers/net/wireless/celeno/cl8k/rfic.c
new file mode 100644
index 000000000000..5a3a595694b5
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/rfic.c
@@ -0,0 +1,232 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include <linux/namei.h>
+
+#include "reg/reg_defs.h"
+#include "phy.h"
+#include "utils.h"
+#include "rfic.h"
+
+#define PHY_DEV_NON_PHYSICAL 0x0
+#define MAX_LOOPS 15
+#define READ_REQ  1
+#define WRITE_REQ 0
+
+int cl_spi_read(struct cl_hw *cl_hw, u8 page, u8 addr, u8 *val)
+{
+	struct mm_spi_read_cfm *cfm;
+	int ret = cl_msg_tx_spi_read(cl_hw, page, addr);
+
+	if (ret)
+		goto out;
+
+	cfm = (struct mm_spi_read_cfm *)(cl_hw->msg_cfm_params[MM_SPI_READ_CFM]);
+	if (!cfm) {
+		ret = -ENOMSG;
+		goto out;
+	}
+
+	if (cfm->status == 0) {
+		*val = cfm->val;
+	} else {
+		cl_dbg_err(cl_hw, "SPI read failed\n");
+		*val = 0;
+	}
+
+	cl_msg_tx_free_cfm_params(cl_hw, MM_SPI_READ_CFM);
+	return 0;
+
+out:
+	*val = 0;
+	return ret;
+}
+
+static int _cl_spi_driver_read(struct cl_hw *cl_hw, u8 more, u8 addr, u8 *val)
+{
+	u8 prescaler = 4;
+	int loops = MAX_LOOPS;
+
+	riu_rc_sw_ctrl_pack(cl_hw, 1, more, 0, 0, 1, 1, 1, 1, prescaler, READ_REQ, addr, 0xFF);
+	while (riu_rc_sw_ctrl_start_done_getf(cl_hw) && --loops)
+		;
+
+	if (!loops) {
+		cl_dbg_verbose(cl_hw, "Read error - addr [0x%02x]\n", addr);
+		return -EBADE;
+	}
+
+	*val = riu_rc_sw_ctrl_data_getf(cl_hw);
+
+	hwreg_pr(cl_hw, "more=%d, addr=0x%x, *val=0x%x\n", more, addr, *val);
+
+	return 0;
+}
+
+static int _cl_spi_driver_write(struct cl_hw *cl_hw, u8 more, u8 addr, u8 val)
+{
+	u8 prescaler =  4;
+	int loops = MAX_LOOPS;
+
+	hwreg_pr(cl_hw, "more=%d, addr=0x%x, val=0x%x\n", more, addr, val);
+
+	riu_rc_sw_ctrl_pack(cl_hw, 1, more, 0, 0, 1, 1, 1, 1, prescaler, WRITE_REQ, addr, val);
+
+	while (riu_rc_sw_ctrl_start_done_getf(cl_hw) && --loops)
+		;
+
+	if (!loops) {
+		cl_dbg_verbose(cl_hw, "Write error - addr [0x%02x] val [0x%02x]\n", addr, val);
+		return -EBADE;
+	}
+
+	return 0;
+}
+
+int cl_spi_driver_read_byte(struct cl_hw *cl_hw, u8 page, u8 addr, u8 *val)
+{
+	/* cl_spi_driver_read_byte should only be used when mac fw not loaded,
+	 * else use cl_spi_read
+	 */
+	int ret = 0;
+
+	spin_lock_bh(&cl_hw->chip->isr_lock);
+
+	ret = _cl_spi_driver_write(cl_hw, 1, 0x03, page);
+	if (ret)
+		goto read_exit;
+
+	ret = _cl_spi_driver_read(cl_hw, 0, addr, val);
+	if (ret)
+		goto read_exit;
+
+read_exit:
+	spin_unlock_bh(&cl_hw->chip->isr_lock);
+
+	return ret;
+}
+
+static u8 cl_rfic_str_to_cmd(struct cl_hw *cl_hw, char *str)
+{
+	if (!strcmp(str, "DONE"))
+		return OVERWRITE_DONE;
+	else if (!strcmp(str, "SPI_R"))
+		return SPI_RD_CMD;
+	else if (!strcmp(str, "SPI_W"))
+		return SPI_WR_CMD;
+	else if (!strcmp(str, "GCU_W"))
+		return GCU_WR_CMD;
+	else if (!strcmp(str, "RIU_W"))
+		return RIU_WR_CMD;
+	else if (!strcmp(str, "GEN_W"))
+		return GEN_WR_CMD;
+	else if (!strcmp(str, "DELAY"))
+		return UDELAY_CMD;
+
+	cl_dbg_err(cl_hw, "unknown command %s\n", str);
+	return OVERWRITE_DONE;
+}
+
+static void cl_parse_rf_command(struct cl_hw *cl_hw, char *str,
+				struct cl_rf_reg_overwrite_info *info)
+{
+	int i = 0;
+	char *ptr = NULL;
+	u32 res = 0;
+
+	while ((ptr = strsep(&str, " ")) && (*ptr != '\n')) {
+		if (i == 0) {
+			info->cmd = cl_rfic_str_to_cmd(cl_hw, ptr);
+		} else {
+			if (kstrtou32(ptr, 16, &res) != 0) {
+				pr_err("%s: invalid data - %s\n", __func__, ptr);
+				return;
+			}
+
+			info->data[i - 1] = cpu_to_le32(res);
+			res = 0;
+		}
+		i++;
+	}
+}
+
+#define RF_CMD_MAX_LEN 64
+
+static void cl_parse_rf_commands_from_buf(struct cl_hw *cl_hw, char *buf, loff_t size,
+					  struct cl_rf_reg_overwrite_info *info)
+{
+	int i = 0;
+	char *line = buf;
+	char str[RF_CMD_MAX_LEN];
+	char *end;
+	int line_length = 0;
+
+	while (line && (line != (buf + size))) {
+		if ((*line == '#') || (*line == '\n')) {
+			/* Skip comment or blank line */
+			line = strstr(line, "\n") + 1;
+		} else if (*line) {
+			end = strstr(line, "\n") + 1;
+			line_length = end - line;
+
+			if (line_length >= RF_CMD_MAX_LEN) {
+				cl_dbg_err(cl_hw, "Command too long (%u)\n", line_length);
+				return;
+			}
+
+			snprintf(str, line_length, "%s", line);
+			cl_parse_rf_command(cl_hw, str, &info[i++]);
+			line += line_length;
+		}
+	}
+}
+
+int cl_rfic_read_overwrite_file(struct cl_hw *cl_hw, struct cl_rf_reg_overwrite_info *info,
+				bool init)
+{
+	char *buf = NULL;
+	size_t size = 0;
+	char filename[CL_FILENAME_MAX] = {0};
+	char path_name[CL_PATH_MAX] = {0};
+	struct path path;
+
+	if (init)
+		snprintf(filename, sizeof(filename), "rf_init_overwrite.txt");
+	else
+		snprintf(filename, sizeof(filename), "rf_tcv%d_overwrite.txt", cl_hw->tcv_idx);
+
+	snprintf(path_name, sizeof(path_name), "/lib/firmware/cl8k/%s", filename);
+	if (kern_path(path_name, LOOKUP_FOLLOW, &path) < 0)
+		return 0;
+
+	size = cl_file_open_and_read(cl_hw->chip, filename, &buf);
+
+	if (!buf)
+		return 0;
+
+	cl_dbg_trace(cl_hw, "parsing %s !!!\n", filename);
+	cl_parse_rf_commands_from_buf(cl_hw, buf, size, info);
+	kfree(buf);
+	return 0;
+}
+
+static u8 cl_rfic_version(struct cl_hw *cl_hw)
+{
+	u8 value = 0xff;
+	int ret = cl_spi_driver_read_byte(cl_hw, 0, 0, &value);
+
+	if (ret < 0)
+		cl_dbg_err(cl_hw, "%s: spi read failed", __func__);
+
+	return value;
+}
+
+void cl_chip_set_rfic_version(struct cl_hw *cl_hw)
+{	/* Read version only on a physical phy */
+	if (cl_hw->chip->conf->ci_phy_dev == PHY_DEV_ATHOS ||
+	    cl_hw->chip->conf->ci_phy_dev == PHY_DEV_OLYMPUS)	{
+		cl_hw->chip->rfic_version = cl_rfic_version(cl_hw);
+	} else {
+		cl_hw->chip->rfic_version = PHY_DEV_NON_PHYSICAL;
+	}
+}