new file mode 100644
@@ -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;
+ }
+}