new file mode 100644
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include "debug.h"
+#include "chip.h"
+#include "rfic.h"
+#include "debug.h"
+#include "version.h"
+
+static int cl_version_request(struct cl_hw *cl_hw)
+{
+ struct mm_version_cfm *cfm = NULL;
+ struct cl_version_db *vd = &cl_hw->version_db;
+ int ret = 0;
+
+ ret = cl_msg_tx_version(cl_hw);
+ if (ret)
+ return ret;
+
+ cfm = (struct mm_version_cfm *)cl_hw->msg_cfm_params[MM_VERSION_CFM];
+ if (!cfm)
+ return -ENOMSG;
+
+ vd->last_update = jiffies;
+ vd->dsp = le32_to_cpu(cfm->versions.dsp);
+ vd->rfic_sw = le32_to_cpu(cfm->versions.rfic_sw);
+ vd->rfic_hw = le32_to_cpu(cfm->versions.rfic_hw);
+ vd->agcram = le32_to_cpu(cfm->versions.agcram);
+
+ cl_hw->rf_crystal_mhz = cfm->rf_crystal_mhz;
+
+ strncpy(vd->fw, cfm->versions.fw, sizeof(vd->fw));
+ vd->fw[sizeof(vd->fw) - 1] = '\0';
+
+ strncpy(vd->drv, CONFIG_CL8K_VERSION, sizeof(vd->drv));
+ vd->drv[sizeof(vd->drv) - 1] = '\0';
+
+ cl_msg_tx_free_cfm_params(cl_hw, MM_VERSION_CFM);
+
+ return ret;
+}
+
+int cl_version_read(struct cl_hw *cl_hw, char *buf, ssize_t buf_size, ssize_t *total_len)
+{
+ struct cl_chip *chip = cl_hw->chip;
+ struct cl_version_db *vd = &cl_hw->version_db;
+ struct cl_agc_profile *agc_profile1 = &cl_hw->phy_data_info.data->agc_params.profile1;
+ struct cl_agc_profile *agc_profile2 = &cl_hw->phy_data_info.data->agc_params.profile2;
+ ssize_t len = 0;
+ int ret = 0;
+ u32 version_agcram = 0;
+ u32 major = 0;
+ u32 minor = 0;
+ u32 internal = 0;
+
+ /* Request data if existing is not actual */
+ if (!vd->last_update) {
+ ret = cl_version_request(cl_hw);
+ if (ret)
+ return ret;
+ }
+
+ /* PHY components specifics */
+ len += scnprintf(buf + len, buf_size - len, "DRV VERSION: %s\n", vd->drv);
+ len += scnprintf(buf + len, buf_size - len, "FW VERSION: %s\n", vd->fw);
+ len += scnprintf(buf + len, buf_size - len, "DSP VERSION: 0x%-.8X\n", vd->dsp);
+ len += scnprintf(buf + len, buf_size - len, "RFIC SW VERSION: %u\n", vd->rfic_sw);
+ len += scnprintf(buf + len, buf_size - len, "RFIC HW VERSION: 0x%X\n", vd->rfic_hw);
+
+ version_agcram = vd->agcram;
+ major = (version_agcram >> 16) & 0xffff;
+ minor = (version_agcram >> 8) & 0xff;
+ internal = version_agcram & 0xff;
+
+ len += scnprintf(buf + len, buf_size - len,
+ "AGC RAM VERSION: B.%x.%x.%x\n", major, minor, internal);
+
+ if (agc_profile1)
+ cl_agc_params_dump_profile_id(buf, buf_size, &len, agc_profile1->id,
+ "AGC PARAMS PROFILE:");
+ if (agc_profile2)
+ cl_agc_params_dump_profile_id(buf, buf_size, &len, agc_profile2->id,
+ "AGC PARAMS PROFILE (Elastic):");
+
+ len += scnprintf(buf + len, buf_size - len,
+ "TX POWER VERSION: %u\n", cl_hw->tx_power_version);
+
+ switch (chip->conf->ci_phy_dev) {
+ case PHY_DEV_OLYMPUS:
+ len += scnprintf(buf + len, buf_size - len, "RFIC TYPE: OLYMPUS\n");
+ break;
+ case PHY_DEV_ATHOS:
+ len += scnprintf(buf + len, buf_size - len, "RFIC TYPE: %s\n",
+ (cl_hw->chip->rfic_version == ATHOS_A_VER) ? "ATHOS" : "ATHOS B");
+ break;
+ case PHY_DEV_DUMMY:
+ len += scnprintf(buf + len, buf_size - len, "RFIC TYPE: DUMMY\n");
+ break;
+ case PHY_DEV_FRU:
+ len += scnprintf(buf + len, buf_size - len, "RFIC TYPE: FRU\n");
+ break;
+ case PHY_DEV_LOOPBACK:
+ len += scnprintf(buf + len, buf_size - len, "RFIC TYPE: LOOPBACK\n");
+ break;
+ }
+
+ len += scnprintf(buf + len, buf_size - len,
+ "RF CRYSTAL: %uMHz\n", cl_hw->rf_crystal_mhz);
+ len += scnprintf(buf + len, buf_size - len,
+ "CHIP ID: 0X%x\n", cl_chip_get_device_id(cl_hw->chip));
+ *total_len = len;
+
+ return 0;
+}
+
+int cl_version_update(struct cl_hw *cl_hw)
+{
+ char *buf = NULL;
+ ssize_t buf_size = PAGE_SIZE;
+ ssize_t len = 0;
+ int ret = 0;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Force logic to update versions */
+ cl_hw->version_db.last_update = 0;
+
+ ret = cl_version_read(cl_hw, buf, buf_size, &len);
+
+ if (ret == 0) {
+ pr_debug("%s\n", buf);
+ /* Share version info */
+ cl_version_sync_wiphy(cl_hw, cl_hw->hw->wiphy);
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
+void cl_version_sync_wiphy(struct cl_hw *cl_hw, struct wiphy *wiphy)
+{
+ strncpy(wiphy->fw_version, cl_hw->version_db.fw, sizeof(wiphy->fw_version));
+}
+