summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/eswin/ecrnx_platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/eswin/ecrnx_platform.c')
-rw-r--r--drivers/net/wireless/eswin/ecrnx_platform.c1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/drivers/net/wireless/eswin/ecrnx_platform.c b/drivers/net/wireless/eswin/ecrnx_platform.c
new file mode 100644
index 000000000000..76295c238105
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_platform.c
@@ -0,0 +1,1107 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_platform.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "ecrnx_platform.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "ecrnx_main.h"
+
+#ifndef CONFIG_ECRNX_ESWIN
+#include "ecrnx_pci.h"
+#ifndef CONFIG_ECRNX_FHOST
+#include "ipc_host.h"
+#endif /* !CONFIG_ECRNX_FHOST */
+#endif /* CONFIG_ECRNX_ESWIN */
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "sdio.h"
+#include "ecrnx_sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "usb.h"
+#include "ecrnx_usb.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "core.h"
+#include "ecrnx_amt.h"
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+/**
+ * ecrnx_plat_tl4_fw_upload() - Load the requested FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a hex file, into the specified address
+ */
+static int ecrnx_plat_tl4_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ const struct firmware *fw;
+ int err = 0;
+ u32 *dst;
+ u8 const *file_data;
+ char typ0, typ1;
+ u32 addr0, addr1;
+ u32 dat0, dat1;
+ int remain;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+ file_data = fw->data;
+ remain = fw->size;
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ /* Walk through all the lines of the configuration file */
+ while (remain >= 16) {
+ u32 data, offset;
+
+ if (sscanf(file_data, "%c:%08X %04X", &typ0, &addr0, &dat0) != 3)
+ break;
+ if ((addr0 & 0x01) != 0) {
+ addr0 = addr0 - 1;
+ dat0 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+ if ((remain < 16) ||
+ (sscanf(file_data, "%c:%08X %04X", &typ1, &addr1, &dat1) != 3) ||
+ (typ1 != typ0) || (addr1 != (addr0 + 1))) {
+ typ1 = typ0;
+ addr1 = addr0 + 1;
+ dat1 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+
+ if (typ0 == 'C') {
+ offset = 0x00200000;
+ if ((addr1 % 4) == 3)
+ offset += 2*(addr1 - 3);
+ else
+ offset += 2*(addr1 + 1);
+
+ data = dat1 | (dat0 << 16);
+ } else {
+ offset = 2*(addr1 - 1);
+ data = dat0 | (dat1 << 16);
+ }
+ dst = (u32 *)(fw_addr + offset);
+ *dst = data;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_bin_fw_upload() - Load the requested binary FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_bin_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ int err = 0;
+ unsigned int i, size;
+ u32 *src, *dst;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = (u32 *)fw->data;
+ dst = (u32 *)fw_addr;
+ size = (unsigned int)fw->size;
+
+ /* check potential platform bug on multiple stores vs memcpy */
+ for (i = 0; i < size; i += 4) {
+ *dst++ = *src++;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+#ifndef CONFIG_ECRNX_TL4
+#define IHEX_REC_DATA 0
+#define IHEX_REC_EOF 1
+#define IHEX_REC_EXT_SEG_ADD 2
+#define IHEX_REC_START_SEG_ADD 3
+#define IHEX_REC_EXT_LIN_ADD 4
+#define IHEX_REC_START_LIN_ADD 5
+
+/**
+ * ecrnx_plat_ihex_fw_upload() - Load the requested intel hex 8 FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a ihex file, into the specified address.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_ihex_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ u8 const *src, *end;
+ u32 *dst;
+ u16 haddr, segaddr, addr;
+ u32 hwaddr;
+ u8 load_fw, byte_count, checksum, csum, rec_type;
+ int err, rec_idx;
+ char hex_buff[9];
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = fw->data;
+ end = src + (unsigned int)fw->size;
+ haddr = 0;
+ segaddr = 0;
+ load_fw = 1;
+ err = -EINVAL;
+ rec_idx = 0;
+ hwaddr = 0;
+
+#define IHEX_READ8(_val, _cs) { \
+ hex_buff[2] = 0; \
+ strncpy(hex_buff, src, 2); \
+ if (kstrtou8(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 2; \
+ if (_cs) \
+ csum += _val; \
+ }
+
+#define IHEX_READ16(_val) { \
+ hex_buff[4] = 0; \
+ strncpy(hex_buff, src, 4); \
+ if (kstrtou16(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 4; \
+ csum += (_val & 0xff) + (_val >> 8); \
+ }
+
+#define IHEX_READ32(_val) { \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, 8); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 8; \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+ }
+
+#define IHEX_READ32_PAD(_val, _nb) { \
+ memset(hex_buff, '0', 8); \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, (2 * _nb)); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += (2 * _nb); \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+}
+
+ /* loop until end of file is read*/
+ while (load_fw) {
+ rec_idx++;
+ csum = 0;
+
+ /* Find next colon start code */
+ while (*src != ':') {
+ src++;
+ if ((src + 3) >= end) /* 3 = : + rec_len */
+ goto end;
+ }
+ src++;
+
+ /* Read record len */
+ IHEX_READ8(byte_count, 1);
+ if ((src + (byte_count * 2) + 8) >= end) /* 8 = rec_addr + rec_type + chksum */
+ goto end;
+
+ /* Read record addr */
+ IHEX_READ16(addr);
+
+ /* Read record type */
+ IHEX_READ8(rec_type, 1);
+
+ switch(rec_type) {
+ case IHEX_REC_DATA:
+ {
+ /* Update destination address */
+ dst = (u32 *) (fw_addr + hwaddr + addr);
+
+ while (byte_count) {
+ u32 val;
+ if (byte_count >= 4) {
+ IHEX_READ32(val);
+ byte_count -= 4;
+ } else {
+ IHEX_READ32_PAD(val, byte_count);
+ byte_count = 0;
+ }
+ *dst++ = __swab32(val);
+ }
+ break;
+ }
+ case IHEX_REC_EOF:
+ {
+ load_fw = 0;
+ err = 0;
+ break;
+ }
+ case IHEX_REC_EXT_SEG_ADD: /* Extended Segment Address */
+ {
+ IHEX_READ16(segaddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_EXT_LIN_ADD: /* Extended Linear Address */
+ {
+ IHEX_READ16(haddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_START_LIN_ADD: /* Start Linear Address */
+ {
+ u32 val;
+ IHEX_READ32(val); /* need to read for checksum */
+ break;
+ }
+ case IHEX_REC_START_SEG_ADD:
+ default:
+ {
+ dev_err(dev, "ihex: record type %d not supported\n", rec_type);
+ load_fw = 0;
+ }
+ }
+
+ /* Read and compare checksum */
+ IHEX_READ8(checksum, 0);
+ if (checksum != (u8)(~csum + 1))
+ goto end;
+ }
+
+#undef IHEX_READ8
+#undef IHEX_READ16
+#undef IHEX_READ32
+#undef IHEX_READ32_PAD
+
+ end:
+ release_firmware(fw);
+
+ if (err)
+ dev_err(dev, "%s: Invalid ihex record around line %d\n", filename, rec_idx);
+
+ return err;
+}
+#endif /* CONFIG_ECRNX_TL4 */
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+/**
+ * ecrnx_plat_get_rf() - Retrun the RF used in the platform
+ *
+ * @ecrnx_plat: pointer to platform structure
+ */
+static u32 ecrnx_plat_get_rf(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 ver;
+ ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+ ver = __MDM_PHYCFG_FROM_VERS(ver);
+ WARN(((ver != MDM_PHY_CONFIG_TRIDENT) &&
+ (ver != MDM_PHY_CONFIG_CATAXIA) &&
+ (ver != MDM_PHY_CONFIG_KARST)),
+ "Unknown PHY version 0x%08x\n", ver);
+
+ return ver;
+}
+#endif
+
+/**
+ * ecrnx_plat_get_clkctrl_addr() - Return the clock control register address
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_SDM
+#ifndef CONFIG_ECRNX_ESWIN
+static u32 ecrnx_plat_get_clkctrl_addr(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 regval;
+ if (ecrnx_plat_get_rf(ecrnx_plat) == MDM_PHY_CONFIG_TRIDENT)
+ return MDM_MEMCLKCTRL0_ADDR;
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ if (__FPGA_TYPE(regval) == 0xC0CA)
+ return CRM_CLKGATEFCTRL0_ADDR;
+ else
+ return MDM_CLKGATEFCTRL0_ADDR;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_stop_agcfsm() - Stop a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: Updated with value of the agccntl rgister before stop
+ * @memclk: Updated with value of the clock register before stop
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_stop_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+ u32 *agcctl, u32 *memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+ /* First read agcctnl and clock registers */
+ *memclk = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+ /* Stop state machine : xxAGCCNTL0[AGCFSMRESET]=1 */
+ *agcctl = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+ ECRNX_REG_WRITE((*agcctl) | BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+
+ /* Force clock */
+ if (agc_ver > 0) {
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=1 */
+ ECRNX_REG_WRITE((*memclk) | BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ } else {
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=0 */
+ ECRNX_REG_WRITE((*memclk) & ~BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ }
+}
+#endif
+
+/**
+ * ecrnx_plat_start_agcfsm() - Restart a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: value of the agccntl register to restore
+ * @memclk: value of the clock register to restore
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_start_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+ u32 agcctl, u32 memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+
+ /* Release clock */
+ if (agc_ver > 0)
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=0 */
+ ECRNX_REG_WRITE(memclk & ~BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ else
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=1 */
+ ECRNX_REG_WRITE(memclk | BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+
+ /* Restart state machine: xxAGCCNTL0[AGCFSMRESET]=0 */
+ ECRNX_REG_WRITE(agcctl & ~BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+}
+#endif
+#endif
+
+/**
+ * ecrnx_plat_get_agc_load_version() - Return the agc load protocol version and the
+ * address of the clock control register
+ *
+ * @ecrnx_plat: platform data
+ * @rf: rf in used
+ * @clkctrladdr: returned clock control register address
+ *
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+static u8 ecrnx_plat_get_agc_load_version(struct ecrnx_plat *ecrnx_plat, u32 rf,
+ u32 *clkctrladdr)
+{
+ u8 agc_load_ver = 0;
+ u32 agc_ver;
+ u32 regval;
+
+ *clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+ /* Trident and Elma PHY use old method */
+ if (rf == MDM_PHY_CONFIG_TRIDENT)
+ return 0;
+
+ /* Get the FPGA signature */
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+
+
+ /* Read RIU version register */
+ agc_ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, RIU_ECRNXVERSION_ADDR);
+ agc_load_ver = __RIU_AGCLOAD_FROM_VERS(agc_ver);
+
+ return agc_load_ver;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_agc_load() - Load AGC ucode
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_agc_load(struct ecrnx_plat *ecrnx_plat)
+{
+ int ret = 0;
+#ifndef CONFIG_ECRNX_SDM
+ u32 agc = 0, agcctl, memclk;
+ u32 clkctrladdr;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ u8 agc_ver;
+
+ switch (rf) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ agc = AGC_ECRNXAGCCNTL_ADDR;
+ break;
+ case MDM_PHY_CONFIG_CATAXIA:
+ case MDM_PHY_CONFIG_KARST:
+ agc = RIU_ECRNXAGCCNTL_ADDR;
+ break;
+ default:
+ return -1;
+ }
+
+ agc_ver = ecrnx_plat_get_agc_load_version(ecrnx_plat, rf, &clkctrladdr);
+
+ ecrnx_plat_stop_agcfsm(ecrnx_plat, agc, &agcctl, &memclk, agc_ver, clkctrladdr);
+
+ ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_AGC_UCODE_ADDR),
+ ECRNX_AGC_FW_NAME);
+
+ if (!ret && (agc_ver == 1)) {
+ /* Run BIST to ensure that the AGC RAM was correctly loaded */
+ ECRNX_REG_WRITE(BIT(28), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_ECRNXDYNAMICCONFIG_ADDR);
+ while (ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_ECRNXDYNAMICCONFIG_ADDR) & BIT(28));
+
+ if (!(ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_AGCMEMBISTSTAT_ADDR) & BIT(0))) {
+ dev_err(ecrnx_platform_get_dev(ecrnx_plat),
+ "AGC RAM not loaded correctly 0x%08x\n",
+ ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_AGCMEMSIGNATURESTAT_ADDR));
+ ret = -EIO;
+ }
+ }
+
+ ecrnx_plat_start_agcfsm(ecrnx_plat, agc, agcctl, memclk, agc_ver, clkctrladdr);
+
+#endif
+ return ret;
+}
+#endif
+
+/**
+ * ecrnx_ldpc_load() - Load LDPC RAM
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ldpc_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ u32 phy_feat = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+ u32 phy_vers = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMVERSION_ADDR);
+
+ if (((rf != MDM_PHY_CONFIG_KARST) && (rf != MDM_PHY_CONFIG_CATAXIA)) ||
+ (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) !=
+ (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) {
+ goto disable_ldpc;
+ }
+ if (__MDM_VERSION(phy_vers) > 30) {
+ return 0;
+ }
+
+ if (ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_LDPC_RAM_ADDR),
+ ECRNX_LDPC_RAM_NAME)) {
+ goto disable_ldpc;
+ }
+
+ return 0;
+
+ disable_ldpc:
+ ecrnx_hw->mod_params->ldpc_on = false;
+
+#endif /* CONFIG_ECRNX_SDM */
+ return 0;
+}
+#endif
+/**
+ * ecrnx_plat_lmac_load() - Load FW code
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_lmac_load(struct ecrnx_plat *ecrnx_plat)
+{
+ int ret;
+
+ #ifdef CONFIG_ECRNX_TL4
+ ret = ecrnx_plat_tl4_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME);
+ #else
+ ret = ecrnx_plat_ihex_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME);
+ if (ret == -ENOENT)
+ {
+ ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME2);
+ }
+ #endif
+
+ return ret;
+}
+#endif
+
+/**
+ * ecrnx_rf_fw_load() - Load RF FW if any
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_rf_fw_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ const struct firmware *fw;
+ int err = 0;
+ u8 const *file_data;
+ int remain;
+ u32 clkforce;
+ u32 clkctrladdr;
+
+ // Today only Cataxia has a FW to load
+ if (rf != MDM_PHY_CONFIG_CATAXIA)
+ return 0;
+
+ err = request_firmware(&fw, ECRNX_CATAXIA_FW_NAME, dev);
+ if (err)
+ {
+ dev_err(dev, "Make sure your board has up-to-date packages.");
+ dev_err(dev, "Run \"sudo smart update\" \"sudo smart upgrade\" commands.\n");
+ return err;
+ }
+
+ file_data = fw->data;
+ remain = fw->size;
+
+ // Get address of clock control register
+ clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+
+ // Force RC clock
+ clkforce = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+ ECRNX_REG_WRITE(clkforce | BIT(27), ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+ mdelay(1);
+
+ // Reset RC
+ ECRNX_REG_WRITE(0x00003100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(20);
+
+ // Reset RF
+ ECRNX_REG_WRITE(0x00133100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(20);
+
+ // Select trx 2 HB
+ ECRNX_REG_WRITE(0x00103100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(1);
+
+ // Set ASP freeze
+ ECRNX_REG_WRITE(0xC1010001, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ mdelay(1);
+
+ /* Walk through all the lines of the FW file */
+ while (remain >= 10) {
+ u32 data;
+
+ if (sscanf(file_data, "0x%08X", &data) != 1)
+ {
+ // Corrupted FW file
+ err = -1;
+ break;
+ }
+ file_data += 11;
+ remain -= 11;
+
+ ECRNX_REG_WRITE(data, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ udelay(50);
+ }
+
+ // Clear ASP freeze
+ ECRNX_REG_WRITE(0xE0010011, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ mdelay(1);
+
+ // Unforce RC clock
+ ECRNX_REG_WRITE(clkforce, ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+ release_firmware(fw);
+
+#endif /* CONFIG_ECRNX_SDM */
+ return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_mpif_sel() - Select the MPIF according to the FPGA signature
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_mpif_sel(struct ecrnx_plat *ecrnx_plat)
+{
+#ifndef CONFIG_ECRNX_SDM
+ u32 regval;
+ u32 type;
+
+ /* Get the FPGA signature */
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ type = __FPGA_TYPE(regval);
+
+ /* Check if we need to switch to the old MPIF or not */
+ if ((type != 0xCAFE) && (type != 0XC0CA) && (regval & 0xF) < 0x3)
+ {
+ /* A old FPGA A is used, so configure the FPGA B to use the old MPIF */
+ ECRNX_REG_WRITE(0x3, ecrnx_plat, ECRNX_ADDR_SYSTEM, FPGAB_MPIF_SEL_ADDR);
+ }
+#endif
+}
+#endif
+
+/**
+ * ecrnx_platform_reset() - Reset the platform
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_platform_reset(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 regval;
+
+ /* the doc states that SOFT implies FPGA_B_RESET
+ * adding FPGA_B_RESET is clearer */
+ ECRNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, ecrnx_plat,
+ ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+ if (regval & SOFT_RESET) {
+ dev_err(ecrnx_platform_get_dev(ecrnx_plat), "reset: failed\n");
+ return -EIO;
+ }
+
+ ECRNX_REG_WRITE(regval & ~FPGA_B_RESET, ecrnx_plat,
+ ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+ return 0;
+}
+#endif
+
+/**
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void* ecrnx_term_save_config(struct ecrnx_plat *ecrnx_plat)
+{
+ const u32 *reg_list;
+ u32 *reg_value, *res;
+ int i, size = 0;
+
+ if (ecrnx_plat->get_config_reg) {
+ size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+ }
+
+ if (size <= 0)
+ return NULL;
+
+ res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+ if (!res)
+ return NULL;
+
+ reg_value = res;
+ for (i = 0; i < size; i++) {
+ *reg_value++ = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+
+ return res;
+}
+#endif
+
+/**
+ * rwmx_platform_restore_config() - Restore hardware config after reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ * @reg_value: Pointer of value to restore
+ * (obtained with rwmx_platform_save_config())
+ *
+ * Restore configuration registers value.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_term_restore_config(struct ecrnx_plat *ecrnx_plat,
+ u32 *reg_value)
+{
+ const u32 *reg_list;
+ int i, size = 0;
+
+ if (!reg_value || !ecrnx_plat->get_config_reg)
+ return;
+
+ size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+
+ for (i = 0; i < size; i++) {
+ ECRNX_REG_WRITE(*reg_value++, ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+}
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_check_fw_compatibility(struct ecrnx_hw *ecrnx_hw)
+{
+ int res = 0;
+
+ struct ipc_shared_env_tag *shared = ecrnx_hw->ipc_env->shared;
+ #ifdef CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = ecrnx_hw->hw->wiphy;
+ #else //CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = ecrnx_hw->wiphy;
+ #endif //CONFIG_ECRNX_SOFTMAC
+ #ifdef CONFIG_ECRNX_OLD_IPC
+ int ipc_shared_version = 10;
+ #else //CONFIG_ECRNX_OLD_IPC
+ int ipc_shared_version = 11;
+ #endif //CONFIG_ECRNX_OLD_IPC
+
+ if(shared->comp_info.ipc_shared_version != ipc_shared_version)
+ {
+ wiphy_err(wiphy, "Different versions of IPC shared version between driver and FW (%d != %d)\n ",
+ ipc_shared_version, shared->comp_info.ipc_shared_version);
+ res = -1;
+ }
+
+ if(shared->comp_info.radarbuf_cnt != IPC_RADARBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Radar events handling "\
+ "between driver and FW (%d != %d)\n", IPC_RADARBUF_CNT,
+ shared->comp_info.radarbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.unsuprxvecbuf_cnt != IPC_UNSUPRXVECBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for unsupported Rx vectors "\
+ "handling between driver and FW (%d != %d)\n", IPC_UNSUPRXVECBUF_CNT,
+ shared->comp_info.unsuprxvecbuf_cnt);
+ res = -1;
+ }
+
+ #ifdef CONFIG_ECRNX_FULLMAC
+ if(shared->comp_info.rxdesc_cnt != IPC_RXDESC_CNT)
+ {
+ wiphy_err(wiphy, "Different number of shared descriptors available for Data RX handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXDESC_CNT,
+ shared->comp_info.rxdesc_cnt);
+ res = -1;
+ }
+ #endif /* CONFIG_ECRNX_FULLMAC */
+
+ if(shared->comp_info.rxbuf_cnt != IPC_RXBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Data Rx handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXBUF_CNT,
+ shared->comp_info.rxbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.msge2a_buf_cnt != IPC_MSGE2A_BUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Emb->App MSGs "\
+ "sending between driver and FW (%d != %d)\n", IPC_MSGE2A_BUF_CNT,
+ shared->comp_info.msge2a_buf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.dbgbuf_cnt != IPC_DBGBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for debug messages "\
+ "sending between driver and FW (%d != %d)\n", IPC_DBGBUF_CNT,
+ shared->comp_info.dbgbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.bk_txq != NX_TXDESC_CNT0)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BK TX queue (%d != %d)\n",
+ NX_TXDESC_CNT0, shared->comp_info.bk_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.be_txq != NX_TXDESC_CNT1)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BE TX queue (%d != %d)\n",
+ NX_TXDESC_CNT1, shared->comp_info.be_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vi_txq != NX_TXDESC_CNT2)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VI TX queue (%d != %d)\n",
+ NX_TXDESC_CNT2, shared->comp_info.vi_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vo_txq != NX_TXDESC_CNT3)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VO TX queue (%d != %d)\n",
+ NX_TXDESC_CNT3, shared->comp_info.vo_txq);
+ res = -1;
+ }
+
+ #if NX_TXQ_CNT == 5
+ if(shared->comp_info.bcn_txq != NX_TXDESC_CNT4)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BCN TX queue (%d != %d)\n",
+ NX_TXDESC_CNT4, shared->comp_info.bcn_txq);
+ res = -1;
+ }
+ #else
+ if (shared->comp_info.bcn_txq > 0)
+ {
+ wiphy_err(wiphy, "BCMC enabled in firmware but disabled in driver\n");
+ res = -1;
+ }
+ #endif /* NX_TXQ_CNT == 5 */
+
+ if(shared->comp_info.ipc_shared_size != sizeof(ipc_shared_env))
+ {
+ wiphy_err(wiphy, "Different sizes of IPC shared between driver and FW (%zd != %d)\n",
+ sizeof(ipc_shared_env), shared->comp_info.ipc_shared_size);
+ res = -1;
+ }
+
+ if(shared->comp_info.msg_api != MSG_API_VER)
+ {
+ wiphy_err(wiphy, "Different supported message API versions between "\
+ "driver and FW (%d != %d)\n", MSG_API_VER, shared->comp_info.msg_api);
+ res = -1;
+ }
+
+ return res;
+}
+#endif /* !CONFIG_ECRNX_ESWIN */
+
+/**
+ * ecrnx_platform_on() - Start the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ */
+int ecrnx_platform_on(struct ecrnx_hw *ecrnx_hw, void *config)
+{
+ u8 *shared_ram;
+ int ret;
+
+ ECRNX_DBG("%s entry!!", __func__);
+ shared_ram = kzalloc(sizeof(struct ipc_shared_env_tag), GFP_KERNEL);
+ if (!shared_ram)
+ return -ENOMEM;
+
+ if ((ret = ecrnx_ipc_init(ecrnx_hw, shared_ram)))
+ return ret;
+
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+ * ecrnx_platform_off() - Stop the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * ecrnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void ecrnx_platform_off(struct ecrnx_hw *ecrnx_hw, void **config)
+{
+ ecrnx_ipc_deinit(ecrnx_hw);
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_deinit(ecrnx_hw);
+#else
+ #error "config error drv";
+#endif
+}
+
+/**
+ * ecrnx_platform_init() - Initialize the platform
+ *
+ * @ecrnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka ecrnx_hw)
+ * That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int ecrnx_platform_init(void *ecrnx_plat, void **platform_data)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+#if defined CONFIG_ECRNX_SOFTMAC
+ return ecrnx_mac80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ return ecrnx_amt_init();
+ }
+ else
+#endif
+ return ecrnx_cfg80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FHOST
+ return ecrnx_fhost_init(ecrnx_plat, platform_data);
+#endif
+}
+
+/**
+ * ecrnx_platform_deinit() - Deinitialize the platform
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void ecrnx_platform_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#if defined CONFIG_ECRNX_SOFTMAC
+ ecrnx_mac80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ ecrnx_amt_deinit();
+ }
+ else
+#endif
+ ecrnx_cfg80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FHOST
+ ecrnx_fhost_deinit(ecrnx_hw);
+#endif
+}
+
+
+/**
+ * ecrnx_platform_register_drv() - Register all possible platform drivers
+ */
+int ecrnx_platform_register_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ return ecrnx_sdio_register_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ return ecrnx_usb_register_drv();
+#else
+ #error "config error drv"
+#endif
+}
+
+
+/**
+ * ecrnx_platform_unregister_drv() - Unegister all platform drivers
+ */
+void ecrnx_platform_unregister_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ return ecrnx_sdio_unregister_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ return ecrnx_usb_unregister_drv();
+#else
+ #error "config error drv"
+#endif
+}
+
+
+#ifndef CONFIG_ECRNX_SDM
+MODULE_FIRMWARE(ECRNX_AGC_FW_NAME);
+MODULE_FIRMWARE(ECRNX_FCU_FW_NAME);
+MODULE_FIRMWARE(ECRNX_LDPC_RAM_NAME);
+#endif
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME);
+#ifndef CONFIG_ECRNX_TL4
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME2);
+#endif