Search Linux Wireless

[RFC v1 088/256] cl8k: add fw/fw_file.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

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

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/fw/fw_file.c | 485 ++++++++++++++++++
 1 file changed, 485 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/fw/fw_file.c

diff --git a/drivers/net/wireless/celeno/cl8k/fw/fw_file.c b/drivers/net/wireless/celeno/cl8k/fw/fw_file.c
new file mode 100644
index 000000000000..73b239ab5814
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/fw/fw_file.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "fw/fw_file.h"
+#include "dbgfile.h"
+#include "reg/reg_access.h"
+#include "chip.h"
+#include <linux/firmware.h>
+
+/* Location where FW codes must be written */
+#define RAM_SMAC_FW_ADDR 0x00300000
+#define RAM_UMAC_FW_ADDR 0x00280000
+#define RAM_LMAC_FW_ADDR 0x00200000
+
+#define FW_START_MAGIC           "CEFWHDRSTART"
+#define FW_END_MAGIC             "CEFWHDREND"
+#define FW_OFFLOAD_MEM_BASE_ADDR 0x70000000 /* Defined in fw link script */
+#define FW_SECTION_SIZE_MASK     0x7FFFF    /* Mask for max. size of a section */
+#define FW_REMOTE_ROM_BASE_ADDR  0x80000000 /* Defined in fw link script */
+#define FW_REMOTE_ROM_MAX        150000
+
+/* Location (offset) where FW codes must be taken from */
+#define IRAM_START_OFFSET        0x40000
+
+/*
+ * Poor man parser of a plain zip file
+ * We use it just as a container for now. Could use cpio instead.
+ * (no compression, no 64-bit data ... no nothing)
+ * Reference: ZIP File Format Specification v.6.3.4 (2014)
+ *     http://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
+ * For BIG ENDIAN host: zip format is always little endian.
+ * TODO need alignment on non-intel hosts! zip format has no alignment,padding
+ * TODO check CRC?
+ */
+
+struct pkzip_local_hdr  {
+       u32 signature;
+       u16 ver2extract;
+       u16 flags;
+       u16 cmpr_meth;
+       u16 filetime;
+       u16 filedate;
+       u32 crc32;
+       u32 cmpr_size;
+       u32 orig_size;
+       u16 fname_len;
+       u16 hdr_extra_len;
+       /* Filename goes here - not 0 terminated! */
+       /* Hdr_extra_data goes here */
+       /* File data goes here; no padding, no alignment */
+} __packed;
+
+#define PKZIP_LOCAL_HDR_MAGIC   le32_to_cpu(0x04034b50)
+#define PKZIP_CENTRAL_DIR_MAGIC le32_to_cpu(0x02014b50)
+
+/*
+ * Enumerate zip data in buffer, find named item
+ * Return: 0 on success (the item found)
+ *        -ENOENT the item not found, normal end of data found
+ *        -EINVAL the data is not zip (maybe old format firmware)
+ *         else invalid data format or other error
+ */
+static int cl_enum_zipfile(const void *data, size_t size,
+                          const char *name, char **pdata, size_t *psize)
+{
+       const struct pkzip_local_hdr *phdr = data;
+       int remain_size = (int)size;
+
+       BUILD_BUG_ON(sizeof(struct pkzip_local_hdr) != 30);
+
+       while (remain_size > sizeof(struct pkzip_local_hdr)) {
+               char *pfname;
+               char *edata;
+
+               if (phdr->signature != PKZIP_LOCAL_HDR_MAGIC) {
+                       if (phdr->signature == PKZIP_CENTRAL_DIR_MAGIC)
+                               return -ENOENT; /* Normal end of zip */
+                       if ((void *)phdr == data)
+                               /* Bad signature in the first entry - not a zip at all */
+                               return -EINVAL;
+                       pr_err("ZIP - unexpected block: %8.8X\n", phdr->signature);
+                       return -1;
+               }
+
+               if (phdr->fname_len == 0 || le16_to_cpu(phdr->fname_len) > 128) {
+                       /* FIX max len */
+                       pr_err("ZIP entry name len bad: %u\n", le16_to_cpu(phdr->fname_len));
+                       return -1;
+               }
+
+               if (phdr->hdr_extra_len == 0) {
+                       pr_err("ZIP xtra hdr size=0! FIXME!\n"); /* Copy name to tmp buffer */
+                       return -1;
+               }
+
+               pfname = (char *)phdr + sizeof(struct pkzip_local_hdr);
+               /* Because fname in zip is not null term! */
+               pfname[le16_to_cpu(phdr->fname_len)] = 0;
+               edata = pfname + le16_to_cpu(phdr->fname_len) + le16_to_cpu(phdr->hdr_extra_len);
+               remain_size -= (sizeof(*phdr) + le16_to_cpu(phdr->fname_len) +
+                               le16_to_cpu(phdr->hdr_extra_len));
+
+               if (phdr->cmpr_size == 0 || le32_to_cpu(phdr->cmpr_size) > remain_size) {
+                       pr_err("ZIP entry data len bad: %u name=%s, left=%u\n",
+                              le32_to_cpu(phdr->cmpr_size), pfname, remain_size);
+                       return -1;
+               }
+
+               if (strncmp(name, pfname, le16_to_cpu(phdr->fname_len)) == 0) {
+                       if (phdr->cmpr_meth != 0 || phdr->cmpr_size != phdr->orig_size) {
+                               pr_err("ZIP entry compressed! name=%s\n", pfname);
+                               return -1;
+                       }
+
+                       *pdata = edata;
+                       *psize = (size_t)le32_to_cpu(phdr->cmpr_size);
+                       return 0;
+               }
+
+               remain_size -= le32_to_cpu(phdr->cmpr_size);
+               phdr = (const struct pkzip_local_hdr *)(edata + le32_to_cpu(phdr->cmpr_size));
+       }
+
+       return -1;
+}
+
+static int cl_fw_unpack(const void *data, size_t size,
+                       const char *name, char **pdata, size_t *psize)
+{
+       /*
+        * Get named item in firmware container
+        * Args: pdata : pointer to pointer to item data, psize : pointer to item size
+        */
+       *pdata = NULL;
+       *psize = 0;
+       return cl_enum_zipfile(data, size, name, pdata, psize);
+}
+
+static int cl_fw_load_other(struct cl_hw *cl_hw, const char *name)
+{
+       /* Handle other stuff in firmware container */
+       char *edata;
+       size_t esize;
+       struct cl_cached_fw *cached_fw = &cl_hw->cached_fw;
+       int rc = cl_fw_unpack(cached_fw->data, cached_fw->size,
+                             name, &edata, &esize);
+
+       if (rc)
+               return rc;
+
+       cl_dbgfile_parse(cl_hw, edata, esize);
+
+       return 0;
+}
+
+/*
+ * Copy the FW code and data into the proper memory inside the firmware asic.
+ * vaddr - run address
+ * paddr - load address
+ * fsize - memory section size to copy
+ * msize - memory section physical size
+ * mem_base - base address of xtensa internal memory
+ * fw_buf - buffer holding the FW binary code and data
+ */
+static void cl_fw_copy_section(struct cl_chip *chip, char *fw_buf, u32 mem_base,
+                              u32 vaddr, u32 paddr, u32 fsize, u32 msize)
+{
+       u32 *src_addr;
+       u32 dst_addr;
+       u32 i;
+
+       src_addr = (u32 *)(fw_buf + (paddr & 0x0007FFFF));
+       /* 512KB - cover all internal iram and dram and some more */
+
+       /* Check if run address is external or internal from xtensa point of view */
+       if ((vaddr & 0xFF000000) == XTENSA_PIF_BASE_ADDR)
+               dst_addr = vaddr & 0x007FFFFF; /* Must be in 8M PCIe window */
+       else
+               dst_addr = (mem_base | (vaddr & 0x0007FFFF));
+
+       for (i = 0; i < fsize; i += sizeof(*src_addr))
+               CL_BAR_REG_WRITE(chip, dst_addr + i, *src_addr++);
+}
+
+static int cl_fw_phdrs_upload(struct cl_chip *chip, struct cl_hw *cl_hw,
+                             u32 fw_addr, const void *edata, size_t esize)
+{
+       /*
+        * Load firmware image with "phdrs" header
+        * and optional non-resident (offloaded) section
+        */
+       u32 size = esize, section, section_cnt = 0;
+       char const *pbuf = edata;
+       u32 *src;
+
+       /* Verify FW image phdrs start magic */
+       if (strncmp(pbuf, FW_START_MAGIC, strlen(FW_START_MAGIC))) {
+               cl_dbg_err(cl_hw, "phdrs start magic not found, aborting...\n");
+               return -1;
+       }
+
+       cl_dbg_info(cl_hw, "phdrs start magic found !!!!!\n");
+       pbuf += (strlen(FW_START_MAGIC) + 1);
+       size -= (strlen(FW_START_MAGIC) + 1);
+
+       /* Verify FW image phdrs end magic */
+       while (size > 0) {
+               if (strncmp(pbuf, FW_END_MAGIC, strlen(FW_END_MAGIC)) == 0) {
+                       cl_dbg_info(cl_hw, "phdrs end magic found !!!!!\n");
+                       break;
+               }
+
+               pbuf += 16;
+               size -= 16;
+               section_cnt++;
+       }
+
+       /* FW image phdrs end magic not found */
+       if (size == 0 || section_cnt > 100) {
+               cl_dbg_err(cl_hw, "phdrs end magic not found, aborting...\n");
+               return -1;
+       }
+
+       /* Remember where the fw code start in firmware buffer */
+       src = (u32 *)(pbuf + (strlen(FW_END_MAGIC) + 1));
+       /* Re-assign firmware buffer ptrs to start */
+       pbuf = edata + (strlen(FW_START_MAGIC) + 1);
+       size = esize - (strlen(FW_START_MAGIC) + 1);
+
+       bool is_offload_present = false;
+       u32 off2_start = 0, off2_end = 0;
+       u32 off3_start = 0, off3_end = 0;
+
+       for (section = 0; section < section_cnt; section++) {
+               u32 *param = (u32 *)pbuf;
+
+               if (le32_to_cpu(param[0]) == FW_REMOTE_ROM_BASE_ADDR) {
+                       if (param[2] > FW_REMOTE_ROM_MAX) {
+                               cl_dbg_info(cl_hw, "%cmac%u: FW remote rom too big = %uK\n",
+                                           cl_hw->fw_prefix, chip->idx, param[2]);
+                       } else {
+                               dma_addr_t phys_dma_addr;
+                               char *pfake = (char *)src + (param[1] & FW_SECTION_SIZE_MASK);
+                               struct cl_dma_accessed *fw_rom = &cl_hw->fw_remote_rom;
+
+                               fw_rom->size = param[2];
+                               fw_rom->drv_v_addr = dma_alloc_coherent(cl_hw->chip->dev,
+                                                                       fw_rom->size,
+                                                                       &phys_dma_addr, GFP_KERNEL);
+                               if (!fw_rom->drv_v_addr) {
+                                       cl_dbg_info(cl_hw, "%cmac%u: FW remote rom dma_alloc_coherent failed = %uK\n",
+                                                   cl_hw->fw_prefix, chip->idx, fw_rom->size);
+                                       fw_rom->size = 0;
+                               } else {
+                                       fw_rom->fw_v_addr = FW_REMOTE_ROM_BASE_ADDR;
+                                       fw_rom->dma_addr = cpu_to_le32(phys_dma_addr);
+                                       memcpy(fw_rom->drv_v_addr, pfake, fw_rom->size);
+                                       cl_dbg_info(cl_hw, "%cmac%u: FW remote rom memory use = %uK\n",
+                                                   cl_hw->fw_prefix, chip->idx, fw_rom->size);
+                               }
+                       }
+                       pbuf += 16;
+                       continue;
+               }
+
+               if (le32_to_cpu(param[0]) == FW_OFFLOAD_MEM_BASE_ADDR) {
+                       is_offload_present = true;
+                       u32 *pdata = (u32 *)((char *)src + (param[1] & 0x7FFFF));
+
+                       off2_start = pdata[0];
+                       off2_end = pdata[1];
+                       off3_start = pdata[2];
+                       off3_end = pdata[3];
+                       cl_dbg_info(cl_hw, "Resident RO DATA block: start=0x%x, end=0x%x\n\n",
+                                   off2_start, off2_end);
+                       pbuf += 16;
+                       continue;
+               }
+
+               cl_fw_copy_section(chip, (char *)src, fw_addr,
+                                  le32_to_cpu(param[0]),
+                                  le32_to_cpu(param[1]),
+                                  le32_to_cpu(param[2]),
+                                  le32_to_cpu(param[3]));
+               pbuf += 16;
+       }
+
+       if (is_offload_present) {
+               /* 2nd pass to find the resident RO data block */
+               pbuf -= (16 * section_cnt);
+               char *resident_file_data = NULL;
+               char *resident_umac_file_data = NULL;
+               u32 *param;
+
+               for (section = 0; section < section_cnt; section++) {
+                       param = (u32 *)pbuf;
+                       if (param[0] <= off2_start &&
+                           (param[0] + param[3]) > off2_end) {
+                               resident_file_data =
+                                       (char *)src + (param[1] & FW_SECTION_SIZE_MASK) +
+                                       (off2_start - param[0]);
+                               cl_dbg_info(cl_hw, "resident_file_data=0x%p.\n",
+                                           resident_file_data);
+                       }
+
+                       if (param[0] <= off3_start &&
+                           (param[0] + param[3]) >= off3_end) {
+                               resident_umac_file_data =
+                                       (char *)src + (param[1] & FW_SECTION_SIZE_MASK) +
+                                       (off3_start - param[0]);
+                               cl_dbg_info(cl_hw, "resident_umac_file_data=0x%p.\n",
+                                           resident_umac_file_data);
+                       }
+
+                       if (param[0] == FW_OFFLOAD_MEM_BASE_ADDR) {
+                               char *pfake = (char *)src + (param[1] & FW_SECTION_SIZE_MASK);
+
+                               cl_dbgfile_store_offload_data(chip,
+                                                             cl_hw,
+                                                             pfake, param[2],
+                                                             FW_OFFLOAD_MEM_BASE_ADDR,
+                                                             resident_file_data,
+                                                             off2_end - off2_start,
+                                                             off2_start,
+                                                             resident_umac_file_data,
+                                                             off3_end - off3_start,
+                                                             off3_start);
+
+                               break; /* This should be last section */
+                       }
+                       pbuf += 16;
+               }
+
+               if (!resident_file_data)
+                       cl_dbg_warn(cl_hw, "FW resident data block [%#X-%#X] not found!\n",
+                                   off2_start, off2_end);
+       }
+
+       return 0;
+}
+
+static int cl_fw_upload(struct cl_chip *chip, struct cl_hw *cl_hw,
+                       u32 fw_addr, const char *data, size_t size)
+{
+       /* Is it old .bin format (used for firmware tests) */
+       if (data[IRAM_START_OFFSET] == 0x06) {
+               const u32 *src = (const u32 *)data;
+               int i;
+
+               for (i = 0; i < size; i += sizeof(*src))
+                       CL_BAR_REG_WRITE(chip, fw_addr + i, *src++);
+
+               return 0;
+       }
+
+       return cl_fw_phdrs_upload(chip, cl_hw, fw_addr, data, size);
+}
+
+static int cl_fw_load_operational(struct cl_hw *cl_hw, const char *fw_name,
+                                 const char *main_str, const char *dbg_str,
+                                 u32 ram_addr)
+{
+       int rc;
+       const struct firmware *fw;
+       char *fw_ptr;
+       size_t fw_size;
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_cached_fw *cached_fw = &cl_hw->cached_fw;
+
+       clear_bit(CL_DEV_FW_SYNC, &cl_hw->drv_flags);
+
+       if (!cached_fw->data) {
+               char path_name[CL_PATH_MAX] = {0};
+
+               snprintf(path_name, sizeof(path_name), "cl8k/%s", fw_name);
+               rc = request_firmware(&fw, path_name, chip->dev);
+
+               if (rc) {
+                       cl_dbg_err(cl_hw, "# Failed to get %s, with error: %x\n",
+                                  path_name, rc);
+                       return rc;
+               }
+               cached_fw->data = vzalloc(fw->size);
+               if (!cached_fw->data) {
+                       release_firmware(fw);
+                       return -ENOMEM;
+               }
+               memcpy(cached_fw->data, fw->data, fw->size);
+               cached_fw->size = fw->size;
+               release_firmware(fw);
+       }
+
+       rc = cl_fw_unpack(cached_fw->data, cached_fw->size,
+                         main_str, &fw_ptr, &fw_size);
+
+       if (rc == 0) {
+               rc = cl_fw_upload(chip, cl_hw, ram_addr,
+                                 fw_ptr, fw_size);
+               /* Load other stuff packed in firmware container */
+               if (rc == 0)
+                       rc = cl_fw_load_other(cl_hw, dbg_str);
+       } else if (rc != -ENOENT) {
+               /* Assume it is a single file, not a container (used for tests) */
+               rc = cl_fw_upload(chip, cl_hw, ram_addr,
+                                 cached_fw->data,
+                                 cached_fw->size);
+       }
+
+       return rc;
+}
+
+static int cl_fw_load_lmac(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+
+       if (cl_fw_load_operational(cl_hw, chip->conf->ce_lmac,
+                                  "lmacfw.main", "lmacfw.dbg",
+                                  RAM_LMAC_FW_ADDR))
+               return -1;
+
+       cl_hw->fw_active = true;
+
+       return 0;
+}
+
+static int cl_fw_load_smac(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+
+       if (cl_fw_load_operational(cl_hw, chip->conf->ce_smac,
+                                  "smacfw.main", "smacfw.dbg",
+                                  RAM_SMAC_FW_ADDR))
+               return -1;
+
+       cl_hw->fw_active = true;
+
+       return 0;
+}
+
+int cl_fw_file_load(struct cl_hw *cl_hw)
+{
+       /* For TCV0 load lmac, and for TCV1 load smac */
+       if (cl_hw_is_tcv0(cl_hw) &&
+           strcmp(cl_hw->chip->conf->ce_lmac, "no_load")) {
+               if (cl_fw_load_lmac(cl_hw))
+                       return -1;
+       } else if (cl_hw_is_tcv1(cl_hw) &&
+                  strcmp(cl_hw->chip->conf->ce_smac, "no_load")) {
+               if (cl_fw_load_smac(cl_hw))
+                       return -1;
+       }
+
+       return 0;
+}
+
+void cl_fw_file_cleanup(struct cl_hw *cl_hw)
+{
+       /* Clean up all firmware allocations in cl_hw */
+       cl_dbgfile_release_mem(&cl_hw->dbg_data, &cl_hw->str_offload_env);
+}
+
+void cl_fw_file_release(struct cl_hw *cl_hw)
+{
+       struct cl_cached_fw *cached_fw = &cl_hw->cached_fw;
+
+       if (cached_fw->data) {
+               struct cl_dma_accessed *fw_rom = &cl_hw->fw_remote_rom;
+
+               vfree(cached_fw->data);
+               cached_fw->data = NULL;
+               cached_fw->size = 0;
+
+               if (fw_rom->drv_v_addr) {
+                       dma_addr_t phys_dma_addr = le32_to_cpu(fw_rom->dma_addr);
+
+                       dma_free_coherent(cl_hw->chip->dev, fw_rom->size, fw_rom->drv_v_addr,
+                                         phys_dma_addr);
+                       fw_rom->drv_v_addr = NULL;
+                       fw_rom->size = 0;
+                       fw_rom->fw_v_addr = 0;
+                       fw_rom->dma_addr = 0;
+               }
+       }
+}
+
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux