diff mbox series

[RFC,v1,065/256] cl8k: add dsp.c

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

Commit Message

Viktor Barna June 17, 2021, 3:59 p.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/dsp.c | 611 +++++++++++++++++++++++++
 1 file changed, 611 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/dsp.c

--
2.30.0
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/dsp.c b/drivers/net/wireless/celeno/cl8k/dsp.c
new file mode 100644
index 000000000000..cf9646cd1ed4
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/dsp.c
@@ -0,0 +1,611 @@ 
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "reg/reg_modem_gcu.h"
+#include "reg/reg_macdsp_api.h"
+#include "reg/reg_cmu.h"
+#include "reg/reg_access.h"
+#include "reg/ceva.h"
+#include "dsp.h"
+#include "hw.h"
+#include <linux/firmware.h>
+
+#define BUSY_WAIT_LIMIT 10000
+
+static int dsp_busy_wait(struct cl_hw *cl_hw, u32 control_reg)
+{
+       int i;
+
+       for (i = 0; i < BUSY_WAIT_LIMIT; i++) {
+               /* Poll Bit29 to verify DMA transfer has ended. */
+               if ((cl_reg_read(cl_hw, control_reg) & 0x20000000) == 0)
+                       return 0;
+
+               cpu_relax();
+       }
+
+       return -EIO;
+}
+
+static void cl_dsp_boot(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+
+       if (cl_hw_is_tcv0(cl_hw)) {
+               /* Disable ceva_free_clk */
+               cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 0);
+               /* Assert Ceva reset */
+               cmu_phy_0_rst_ceva_0_global_rst_n_setf(chip, 0);
+       } else {
+               /* Disable ceva_free_clk */
+               cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 0);
+               /* Assert Ceva reset */
+               cmu_phy_1_rst_ceva_1_global_rst_n_setf(chip, 0);
+       }
+
+       /* Set Ceva boot=1 */
+       modem_gcu_ceva_ctrl_boot_setf(cl_hw, 1);
+       /* Set Ceva vector */
+       modem_gcu_ceva_vec_set(cl_hw, 0);
+
+       if (cl_hw_is_tcv0(cl_hw)) {
+               /* Enable ceva_clk */
+               cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 1);
+               /* Disabel ceva_clk */
+               cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 0);
+               /* De-Assert Ceva reset - Reset Release */
+               cmu_phy_0_rst_ceva_0_global_rst_n_setf(chip, 1);
+               /* Enable ceva_clk */
+               cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 1);
+       } else {
+               /* Enable ceva_clk */
+               cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 1);
+               /* Disabel ceva_clk */
+               cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 0);
+               /* De-Assert Ceva reset - Reset Release */
+               cmu_phy_1_rst_ceva_1_global_rst_n_setf(chip, 1);
+               /* Enable ceva_clk */
+               cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 1);
+       }
+
+       /* Release Ceva external_wait */
+       modem_gcu_ceva_ctrl_external_wait_setf(cl_hw, 0);
+       /* Set Ceva boot=0 */
+       modem_gcu_ceva_ctrl_boot_setf(cl_hw, 0);
+}
+
+static void config_dma_for_code_copy(struct cl_hw *cl_hw, u32 page)
+{
+       /* Configure Program DMA to copy FW code from Shared PMEM to internal PMEM. */
+
+       /* External address to read from. */
+       cl_reg_write(cl_hw, CEVA_CPM_PDEA_REG, CEVA_SHARED_PMEM_BASE_ADDR_INTERNAL);
+       /* Internal address to write to. */
+       cl_reg_write(cl_hw, CEVA_CPM_PDIA_REG, CEVA_SHARED_PMEM_SIZE * page);
+       /* Page size */
+       cl_reg_write(cl_hw, CEVA_CPM_PDTC_REG, CEVA_SHARED_PMEM_SIZE);
+}
+
+static void config_dma_for_external_data_copy(struct cl_hw *cl_hw)
+{
+       /* Configure Program DMA to copy FW code from Shared XMEM to internal XMEM. */
+
+       /* External address to read from. */
+       cl_reg_write(cl_hw, CEVA_CPM_DDEA_REG, CEVA_SHARED_XMEM_BASE_ADDR_INTERNAL);
+       /* Internal address to write to. */
+       cl_reg_write(cl_hw, CEVA_CPM_DDIA_REG, 0);
+       /* Page size + DMA direction is write */
+       cl_reg_write(cl_hw, CEVA_CPM_DDTC_REG,
+                    CEVA_SHARED_XMEM_SIZE | CEVA_CPM_DDTC_WRITE_COMMAND);
+}
+
+static int cl_dsp_hex_load(struct cl_hw *cl_hw, const u8 *buf,
+                          off_t offset, size_t size, size_t buf_size)
+{
+       u8 single_buf[4] = {0};
+       u32 bin_data = 0;
+       u8 next_byte;
+       u8 byte_num = 0;
+       int ret = 0;
+       ssize_t oft = 0;
+       size_t real_size = min(size * 3, buf_size);
+       /*
+        * CEVA_SHARED_PMEM_BASE_ADDR_FROM_HOST is global and we don't
+        * want to add TCV reg offset.
+        */
+       bool chip_reg = (offset == CEVA_SHARED_PMEM_BASE_ADDR_FROM_HOST);
+
+       if (buf_size % 3) {
+               cl_dbg_err(cl_hw, "DSP size %zu must be divided by 3 !!!\n",
+                          buf_size);
+               return -EINVAL;
+       }
+
+       while (oft < real_size) {
+               memcpy(single_buf, buf + oft, 3);
+               /* Each line contains 2 hex digits + a line feed, i.e. 3 bytes */
+               ret = kstrtou8(single_buf, 16, &next_byte);
+               if (ret < 0) {
+                       cl_dbg_err(cl_hw,
+                                  "ret = %d, oft = %zu,"
+                                  "single_buf = 0x%x 0x%x 0x%x 0x%x\n",
+                                  ret, oft, single_buf[0], single_buf[1],
+                                  single_buf[2], single_buf[3]);
+                       return ret;
+               }
+
+               /* Little-endian order. */
+               bin_data += next_byte << (8 * byte_num);
+               byte_num = (byte_num + 1) % 4;
+
+               /* Read 4 lines from the file, and then write. */
+               if (byte_num == 0) {
+                       if (chip_reg)
+                               cl_reg_write_chip(cl_hw->chip, offset, bin_data);
+                       else
+                               cl_reg_write_direct(cl_hw, offset, bin_data);
+                       offset += 4;
+                       bin_data = 0;
+               }
+
+               memset(&single_buf, 0, sizeof(single_buf));
+               oft += 3;
+       }
+
+       return 0;
+}
+
+static int load_dsp_code(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       u32 real_size;
+       u32 page;
+       const struct firmware *fw;
+       size_t size = 0;
+       u8 *buf = NULL;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s",
+                cl_hw->conf->ce_dsp_code);
+
+       cl_dbg_verbose(cl_hw, "from %s\n", cl_hw->conf->ce_dsp_code);
+
+       ret = request_firmware(&fw, path_name, chip->dev);
+
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to get %s, with error: %x!\n",
+                          path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+       buf = (u8 *)fw->data;
+
+       for (page = 0; page < CEVA_MAX_PAGES; page++) {
+               /* Copy DSP code (one page each time) */
+               ret =  cl_dsp_hex_load(cl_hw, buf,
+                                      CEVA_SHARED_PMEM_BASE_ADDR_FROM_HOST,
+                                      CEVA_SHARED_PMEM_SIZE, size);
+               if (ret != 0) {
+                       cl_dbg_err(cl_hw, "Failed to load pmem page 0x%x!\n", page);
+                       break;
+               }
+
+               config_dma_for_code_copy(cl_hw, page);
+               ret = dsp_busy_wait(cl_hw, CEVA_CPM_PDTC_REG);
+
+               if (ret) {
+                       cl_dbg_err(cl_hw, "dsp_busy_wait failed!\n");
+                       goto out;
+               }
+
+               real_size = min_t(u32, CEVA_SHARED_PMEM_SIZE * 3, size);
+               buf += real_size;
+               size -= real_size;
+       }
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int load_dsp_data(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       const struct firmware *fw;
+       size_t size = 0;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s",
+                cl_hw->conf->ce_dsp_data);
+
+       cl_dbg_verbose(cl_hw, "from %s\n", cl_hw->conf->ce_dsp_data);
+
+       ret = request_firmware(&fw, path_name, chip->dev);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to get %s, with error: %x!\n",
+                          path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+
+       ret = cl_dsp_hex_load(cl_hw, fw->data, REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_DATA_SIZE, size);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load HEX file\n");
+               goto out;
+       }
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int load_dsp_external_data(struct cl_hw *cl_hw)
+{
+       /*
+        * Shared XMEM is not accessible by host.
+        * Copy the XMEM section to DRAM first and then use CEVA internal DMA to copy to
+        * SHARED XMEM.
+        */
+       struct cl_chip *chip = cl_hw->chip;
+       const struct firmware *fw;
+       size_t size = 0;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s",
+                cl_hw->conf->ce_dsp_external_data);
+
+       cl_dbg_verbose(cl_hw, "from %s\n", cl_hw->conf->ce_dsp_external_data);
+
+       ret = request_firmware(&fw, path_name, chip->dev);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to get %s, with error: %x!\n",
+                          path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+
+       ret = cl_dsp_hex_load(cl_hw, fw->data, REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_EXT_DATA_SIZE, size);
+
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load HEX file\n");
+               goto out;
+       }
+
+       config_dma_for_external_data_copy(cl_hw);
+       ret = dsp_busy_wait(cl_hw, CEVA_CPM_DDTC_REG);
+
+       if (ret) {
+               cl_dbg_err(cl_hw, "dsp_busy_wait failed!\n");
+               goto out;
+       }
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static bool cl_dsp_is_universal_file(struct cl_chip *chip)
+{
+       return (cl_chip_is_tcv0_enabled(chip) &&
+               cl_chip_is_tcv1_enabled(chip) &&
+               !strcmp(chip->cl_hw_tcv0->conf->ce_dsp_code,
+                       chip->cl_hw_tcv1->conf->ce_dsp_code));
+}
+
+static int load_dsp_code_dual(struct cl_chip *chip, const char *filename)
+{
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+       u32 real_size;
+       u32 page;
+       const struct firmware *fw;
+       size_t size = 0;
+       u8 *buf = NULL;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s", filename);
+       cl_dbg_chip_verbose(chip, "from %s\n", filename);
+       ret = request_firmware(&fw, path_name, chip->dev);
+
+       if (ret) {
+               cl_dbg_chip_err(chip, "Failed to get %s, with error: %x!\n",
+                               path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+       buf = (u8 *)fw->data;
+
+       for (page = 0; page < CEVA_MAX_PAGES; page++) {
+               /* Copy DSP code (one page each time) */
+               ret = cl_dsp_hex_load(chip->cl_hw_tcv0, buf,
+                                     CEVA_SHARED_PMEM_BASE_ADDR_FROM_HOST,
+                                     CEVA_SHARED_PMEM_SIZE, size);
+               if (ret) {
+                       cl_dbg_chip_err(chip, "Failed to load pmem page 0x%x!\n", page);
+                       break;
+               }
+
+               config_dma_for_code_copy(cl_hw_tcv0, page);
+               ret = dsp_busy_wait(cl_hw_tcv0, CEVA_CPM_PDTC_REG);
+
+               if (ret) {
+                       cl_dbg_err(cl_hw_tcv0, "dsp_busy_wait failed\n");
+                       goto out;
+               }
+
+               config_dma_for_code_copy(cl_hw_tcv1, page);
+               ret = dsp_busy_wait(cl_hw_tcv1, CEVA_CPM_PDTC_REG);
+
+               if (ret) {
+                       cl_dbg_err(cl_hw_tcv1, "dsp_busy_wait failed\n");
+                       goto out;
+               }
+
+               real_size = min_t(u32, CEVA_SHARED_PMEM_SIZE * 3, size);
+               buf += real_size;
+               size -= real_size;
+       }
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int load_dsp_external_data_dual(struct cl_chip *chip, const char *filename)
+{
+       /*
+        * Shared XMEM is not accessible by host.
+        * Copy the XMEM section to DRAM first and then use CEVA internal DMA to copy to
+        * SHARED XMEM.
+        */
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+       const struct firmware *fw;
+       size_t size = 0;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s", filename);
+       cl_dbg_chip_verbose(chip, "from %s\n", filename);
+       ret = request_firmware(&fw, path_name, chip->dev);
+
+       if (ret) {
+               cl_dbg_chip_err(chip, "Failed to get %s, with error: %x!\n",
+                               path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+
+       /* TCV0 */
+       ret = cl_dsp_hex_load(cl_hw_tcv0, fw->data, REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_EXT_DATA_SIZE, size);
+
+       if (ret) {
+               cl_dbg_err(cl_hw_tcv0, "Failed to load HEX file\n");
+               goto out;
+       }
+
+       config_dma_for_external_data_copy(cl_hw_tcv0);
+       ret = dsp_busy_wait(cl_hw_tcv0, CEVA_CPM_DDTC_REG);
+
+       if (ret) {
+               cl_dbg_err(cl_hw_tcv0, "dsp_busy_wait failed!\n");
+               goto out;
+       }
+
+       /* TCV1 */
+       ret = cl_dsp_hex_load(cl_hw_tcv1, fw->data, REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_EXT_DATA_SIZE, size);
+
+       if (ret) {
+               cl_dbg_err(cl_hw_tcv1, "Failed to load HEX file\n");
+               goto out;
+       }
+
+       config_dma_for_external_data_copy(cl_hw_tcv1);
+       ret = dsp_busy_wait(cl_hw_tcv1, CEVA_CPM_DDTC_REG);
+
+       if (ret) {
+               cl_dbg_err(cl_hw_tcv1, "dsp_busy_wait failed!\n");
+               goto out;
+       }
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int load_dsp_data_dual(struct cl_chip *chip, const char *filename)
+{
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+       const struct firmware *fw;
+       size_t size = 0;
+       char path_name[CL_PATH_MAX] = {0};
+       int ret;
+
+       snprintf(path_name, sizeof(path_name), "cl8k/%s", filename);
+       cl_dbg_chip_verbose(chip, "from %s\n", filename);
+       ret = request_firmware(&fw, path_name, chip->dev);
+
+       if (ret) {
+               cl_dbg_chip_err(chip, "Failed to get %s, with error: %x!\n",
+                               path_name, ret);
+               goto out;
+       }
+
+       size = fw->size;
+
+       ret = cl_dsp_hex_load(cl_hw_tcv0, fw->data,
+                             REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_DATA_SIZE, size);
+
+       if (ret != 0) {
+               cl_dbg_err(cl_hw_tcv0, "Failed to load HEX file\n");
+               goto out;
+       }
+
+       ret = cl_dsp_hex_load(cl_hw_tcv1, fw->data,
+                             REG_MACDSP_API_BASE_ADDR,
+                             CEVA_DSP_DATA_SIZE, size);
+
+       if (ret != 0) {
+               cl_dbg_err(cl_hw_tcv1, "Failed to load HEX file\n");
+               goto out;
+       }
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static void print_ceva_core_info(struct cl_hw *cl_hw)
+{
+       cl_dbg_trace(cl_hw, "CEVA_CORE_VERSION_ADDR=0x%X.\n",
+                    cl_reg_read(cl_hw, CEVA_CORE_VERSION_ADDR));
+       cl_dbg_trace(cl_hw, "CEVA_CORE_ID_ADDR=0x%X.\n",
+                    cl_reg_read(cl_hw, CEVA_CORE_ID_ADDR));
+}
+
+static int cl_dsp_load_dual(struct cl_chip *chip)
+{
+       int ret = 0;
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+       struct cl_tcv_conf *tcv0_conf = cl_hw_tcv0->conf;
+
+       modem_gcu_ceva_ctrl_external_wait_setf(cl_hw_tcv0, 0x1);
+       modem_gcu_ceva_ctrl_external_wait_setf(cl_hw_tcv1, 0x1);
+
+       print_ceva_core_info(cl_hw_tcv0);
+       print_ceva_core_info(cl_hw_tcv1);
+
+       ret = load_dsp_code_dual(chip, tcv0_conf->ce_dsp_code);
+       if (ret != 0) {
+               cl_dbg_chip_err(chip,
+                               "Failed to load DSP code. Error code %d.\n",
+                               ret);
+               return ret;
+       }
+
+       ret = load_dsp_external_data_dual(chip, tcv0_conf->ce_dsp_external_data);
+       if (ret != 0) {
+               cl_dbg_chip_err(chip,
+                               "Failed to load DSP external data. Error code %d.\n",
+                               ret);
+               return ret;
+       }
+
+       ret = load_dsp_data_dual(chip, tcv0_conf->ce_dsp_data);
+       if (ret != 0) {
+               cl_dbg_chip_err(chip,
+                               "Failed to load DSP data. Error code %d.\n",
+                               ret);
+               return ret;
+       }
+
+       macdsp_api_config_space_set(cl_hw_tcv0, 0);
+       /* Release DSP wait. */
+       cl_dsp_boot(cl_hw_tcv0);
+
+       macdsp_api_config_space_set(cl_hw_tcv1, 0);
+       /* Release DSP wait. */
+       cl_dsp_boot(cl_hw_tcv1);
+
+       return ret;
+}
+
+static int _cl_dsp_load(struct cl_hw *cl_hw)
+{
+       int ret = 0;
+
+       modem_gcu_ceva_ctrl_external_wait_setf(cl_hw, 0x1);
+       print_ceva_core_info(cl_hw);
+
+       ret = load_dsp_code(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load DSP code %d\n", ret);
+               return ret;
+       }
+
+       ret = load_dsp_external_data(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load DSP external data %d\n", ret);
+               return ret;
+       }
+
+       ret = load_dsp_data(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load DSP data %d\n", ret);
+               return ret;
+       }
+
+       macdsp_api_config_space_set(cl_hw, 0);
+       /* Release DSP wait */
+       cl_dsp_boot(cl_hw);
+
+       return ret;
+}
+
+int cl_dsp_load_regular(struct cl_chip *chip)
+{
+       int ret = 0;
+
+       if (cl_dsp_is_universal_file(chip))
+               return cl_dsp_load_dual(chip);
+
+       if (cl_chip_is_tcv0_enabled(chip)) {
+               ret = _cl_dsp_load(chip->cl_hw_tcv0);
+               if (ret)
+                       return ret;
+       }
+
+       if (cl_chip_is_tcv1_enabled(chip)) {
+               ret = _cl_dsp_load(chip->cl_hw_tcv1);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+int cl_dsp_load_recovery(struct cl_hw *cl_hw)
+{
+       int ret = 0;
+
+       modem_gcu_ceva_ctrl_external_wait_setf(cl_hw, 0x1);
+
+       ret = load_dsp_external_data(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load DSP external data %d\n", ret);
+               return ret;
+       }
+
+       ret = load_dsp_data(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Failed to load DSP data %d\n", ret);
+               return ret;
+       }
+
+       macdsp_api_config_space_set(cl_hw, 0);
+       /* Release DSP wait. */
+       cl_dsp_boot(cl_hw);
+
+       return ret;
+}