luckfox-pico-sdk/sysdrv/drv_ko/wifi/ssv6x5x/smac/hal/hal.c
2023-08-08 20:36:47 +08:00

882 lines
28 KiB
C

/*
* Copyright (c) 2015 iComm-semi Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef ECLIPSE
#include <ssv_mod_conf.h>
#endif // ECLIPSE
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <ssv_chip_id.h>
#include <ssv6200.h>
#include <smac/dev.h>
#include <hal.h>
#include <smac/ssv_skb.h>
#include <hci/hctrl.h>
//include firmware binary header
#include <include/ssv6x5x-sw.h>
static void ssv6xxx_cmd_cali(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_cmd_rc(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_cmd_efuse(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_set_sifs(struct ssv_hw *sh, int band)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_cmd_hwinfo(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static int ssv6xxx_get_tkip_mmic_err(struct sk_buff *skb)
{
return 0;
}
static void ssv6xxx_cmd_txgen(struct ssv_hw *sh, u8 drate)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_cmd_rf(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_cmd_hwq_limit(struct ssv_hw *sh, int argc, char *argv[])
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_init_gpio_cfg(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_flash_read_all_map(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_write_efuse(struct ssv_hw *sh, u8 *data, u8 data_length)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_update_rf_table(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
void ssv6xxx_set_on3_enable(struct ssv_hw *sh, bool val)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static int ssv6xxx_init_hci_rx_aggr(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
return -1;
}
static int ssv6xxx_reset_hw_mac(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
return -1;
}
static void ssv6xxx_set_crystal_clk(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_wait_usb_rom_ready(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_detach_usb_hci(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_pll_chk(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static bool ssv6xxx_put_mic_space_for_hw_ccmp_encrypt(struct ssv_softc *sc, struct sk_buff *skb)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
return false;
}
#ifdef CONFIG_PM
static void ssv6xxx_save_clear_trap_reason(struct ssv_softc *sc)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_restore_trap_reason(struct ssv_softc *sc)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_ps_save_reset_rx_flow(struct ssv_softc *sc)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_ps_restore_rx_flow(struct ssv_softc *sc)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_pmu_awake(struct ssv_softc *sc)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_ps_hold_on3(struct ssv_softc *sc, int value)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
#endif
static int ssv6xxx_get_sram_mode(struct ssv_hw *sh)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
return 0;
}
static void ssv6xxx_rc_mac80211_tx_rate_idx(struct ssv_softc *sc, int hw_rate_idx, struct ieee80211_tx_info *tx_info)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_update_mac80211_chan_info(struct ssv_softc *sc,
struct sk_buff *skb, struct ieee80211_rx_status *rxs)
{
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_read_allid_map(struct ssv_hw *sh, u32 *id0, u32 *id1, u32 *id2, u32 *id3)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_read_txid_map(struct ssv_hw *sh, u32 *id0, u32 *id1, u32 *id2, u32 *id3)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void ssv6xxx_read_rxid_map(struct ssv_hw *sh, u32 *id0, u32 *id1, u32 *id2, u32 *id3)
{
struct ssv_softc *sc = sh->sc;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL,
"%s is not supported for this model!!\n",__func__);
}
static void *ssv6xxx_open_firmware(char *user_mainfw)
{
struct file *fp;
fp = filp_open(user_mainfw, O_RDONLY, 0);
if (IS_ERR(fp))
fp = NULL;
return fp;
}
static int ssv6xxx_read_fw_block(char *buf, int len, void *image)
{
struct file *fp = (struct file *)image;
int rdlen;
if (!image)
return 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
rdlen = kernel_read(fp, buf, len, &fp->f_pos);
#else
rdlen = kernel_read(fp, fp->f_pos, buf, len);
if (rdlen > 0)
fp->f_pos += rdlen;
#endif
return rdlen;
}
static void ssv6xxx_close_firmware(void *image)
{
if (image)
filp_close((struct file *)image, NULL);
}
static u32 _ssv6xxx_get_sram_mapping_address(bool need_chk, u32 sram_addr)
{
#define REMAPPING_ADDR_THRESHOLD (0x00010000)
#define EXTRA_MAPPING_ADDR (0x00100000-REMAPPING_ADDR_THRESHOLD)
if((true == need_chk) && (REMAPPING_ADDR_THRESHOLD <= sram_addr))
{
return sram_addr+EXTRA_MAPPING_ADDR;
}
return sram_addr;
}
static int _ssv6xxx_load_firmware(struct ssv_hw *sh, u8 *firmware_name, u8 openfile)
{
int ret = 0;
u8 *fw_buffer = NULL;
u32 sram_addr = FW_START_SRAM_ADDR;
u32 block_count = 0;
u32 res_size=0, len=0, tolen=0;
void *fw_fp = NULL;
u8 interface = HCI_DEVICE_TYPE(sh->hci.hci_ctrl);
#ifdef ENABLE_FW_SELF_CHECK
u32 checksum = FW_CHECKSUM_INIT;
u32 fw_checksum, fw_clkcnt;
u32 retry_count = 3;
u32 *fw_data32;
#else
int writesize = 0;
u32 retry_count = 1;
#endif
u32 word_count, i;
u32 fw_res_size = ssv6x5x_sw_bin_len;
u32 rsize = 0;
bool need_chk_map = false;
struct ssv_softc *sc = sh->sc;
//if (hci_ctrl->redownload == 1) {
/* force mcu jump to rom for always*/
// Redownload firmware
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Re-download FW\n");
if (interface == SSV_HWIF_INTERFACE_USB)
HAL_JUMP_TO_ROM(sh->sc);
//}
/* Get SRAM mapping mode:
* 0: ILM 64KB, DLM 128KB
* 1: ILM 160KB, DLM 32KB
*/
if(HAL_GET_SRAM_MODE(sh) == 0)
{
need_chk_map = true;
}
else
{
need_chk_map = false;
}
// Load firmware
if(openfile)
{
fw_fp = ssv6xxx_open_firmware(firmware_name);
if (!fw_fp) {
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "failed to find firmware (%s)\n", firmware_name);
ret = -1;
goto out;
}
}
else
{
fw_fp = (void *)ssv6x5x_sw_bin;
}
// Allocate buffer firmware aligned with FW_BLOCK_SIZE and padding with 0xA5 in empty space.
fw_buffer = (u8 *)kzalloc(FW_BLOCK_SIZE, GFP_KERNEL);
if (fw_buffer == NULL) {
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Failed to allocate buffer for firmware.\n");
goto out;
}
do {
#ifdef ENABLE_FW_SELF_CHECK
checksum = FW_CHECKSUM_INIT;
tolen = 0;
if (openfile)
((struct file *)fw_fp)->f_pos = 0;
#endif
// Disable MCU (USB ROM code must be alive for downloading FW, so USB doesn't do it)
if (!(interface == SSV_HWIF_INTERFACE_USB)) {
ret = HAL_LOAD_FW_DISABLE_MCU(sh);
if (ret == -1)
goto out;
}
// Write firmware to SRAM address 0
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Writing firmware to SSV6XXX...\n");
memset(fw_buffer, 0xA5, CHECKSUM_BLOCK_SIZE);
while(fw_res_size)
{
rsize = (fw_res_size>CHECKSUM_BLOCK_SIZE)?CHECKSUM_BLOCK_SIZE:fw_res_size;
if(openfile)
{
if(!(len = ssv6xxx_read_fw_block((char*)fw_buffer, rsize, fw_fp)))
{
break;
}
}
else
{
if(0 == fw_res_size)
{
break;
}
len = rsize;
memcpy((void *)fw_buffer, (const void *)fw_fp, len);
fw_fp += CHECKSUM_BLOCK_SIZE;
fw_res_size -= len;
}
tolen += len;
//printk("read len=%d,sram_addr=%d\n",len,sram_addr);
//if (len < CHECKSUM_BLOCK_SIZE) {
// res_size = len;
// break;
//}
fw_data32 = (u32 *)fw_buffer;
//Add to avoid USB load code with turismo C0/D0 failed, skip 0x10000~0x10aff.
if((SSV_HWIF_INTERFACE_USB == interface) &&
(0x10000 <= sram_addr) && (0x10b00 > sram_addr))
{
if(0x10b00 < (sram_addr+len))
{
if ((ret = SMAC_LOAD_FW(sh, _ssv6xxx_get_sram_mapping_address(need_chk_map, 0x10b00), (u8 *)fw_buffer+(0x10b00-sram_addr), (sram_addr+len-0x10b00))) != 0)
{
break;
}
word_count = (CHECKSUM_BLOCK_SIZE / sizeof(u32));
for (i = ((0x10b00-sram_addr)/sizeof(u32)); i < word_count; i++) {
checksum += fw_data32[i];
}
}
}
else
{
if ((ret = SMAC_LOAD_FW(sh, _ssv6xxx_get_sram_mapping_address(need_chk_map, sram_addr), (u8 *)fw_buffer, CHECKSUM_BLOCK_SIZE)) != 0)
{
break;
}
word_count = (CHECKSUM_BLOCK_SIZE / sizeof(u32));
for (i = 0; i < word_count; i++) {
checksum += fw_data32[i];
}
}
sram_addr += CHECKSUM_BLOCK_SIZE;
//printk("\nper blk cks=%x\n",checksum);
//memset(fw_buffer, 0xA5, CHECKSUM_BLOCK_SIZE);
}
// Calculate the final checksum.
checksum = ((checksum >> 24) + (checksum >> 16) + (checksum >> 8) + checksum) & 0x0FF;
checksum <<= 16;
if (ret == 0) {
// Reset CPU for USB switching ROM to firmware
if (interface == SSV_HWIF_INTERFACE_USB) {
ret = HAL_RESET_CPU(sh);
if (ret == -1)
goto out;
}
// Set ILM/DLM
HAL_SET_SRAM_MODE(sh, SRAM_MODE_ILM_160K_DLM_32K);
block_count = tolen / CHECKSUM_BLOCK_SIZE;
res_size = tolen % CHECKSUM_BLOCK_SIZE;
if(res_size)
block_count++;
// Inform FW that how many blocks is downloaded such that FW can calculate the checksum.
HAL_LOAD_FW_SET_STATUS(sh, (block_count << 16));
HAL_LOAD_FW_GET_STATUS(sh, &fw_clkcnt);
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "(block_count << 16) = %x,reg =%x\n", (block_count << 16),fw_clkcnt);
// Release reset to let CPU run.
HAL_LOAD_FW_ENABLE_MCU(sh);
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Firmware \"%s\" loaded\n", firmware_name);
// Wait FW to calculate checksum.
msleep(50);
// Check checksum result and set to complement value if checksum is OK.
HAL_LOAD_FW_GET_STATUS(sh, &fw_checksum);
fw_checksum = fw_checksum & FW_STATUS_MASK;
if (fw_checksum == checksum) {
HAL_LOAD_FW_SET_STATUS(sh, (~checksum & FW_STATUS_MASK));
ret = 0;
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Firmware check OK.%04x = %04x\n", fw_checksum, checksum);
break;
} else {
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "FW checksum error: %04x != %04x\n", fw_checksum, checksum);
ret = -1;
}
} else {
dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_HAL, "Firmware \"%s\" download failed. (%d)\n", firmware_name, ret);
ret = -1;
}
} while (--retry_count);
if (ret)
goto out;
// hci_ctrl->redownload = 1;
ret = 0;
out:
if(openfile)
{
if(fw_fp)
{
ssv6xxx_close_firmware(fw_fp);
}
}
if (fw_buffer != NULL)
{
kfree(fw_buffer);
}
return ret;
}
static int ssv6xxx_hci_load_firmware_openfile(struct ssv_hw *sh, u8 *firmware_name)
{
return _ssv6xxx_load_firmware(sh, firmware_name, 1);
}
#if 0 //Keep it, but not used.
static int ssv6xxx_hci_get_firmware(struct device *dev, char *user_mainfw, const struct firmware **mainfw)
{
int ret;
//hBUG_ON(helper == NULL);
BUG_ON(mainfw == NULL);
/* Try user-specified firmware first */
if (*user_mainfw) {
ret = request_firmware(mainfw, user_mainfw, dev);
if (ret) {
goto fail;
}
if (*mainfw)
return 0;
}
fail:
/* Failed */
if (*mainfw) {
release_firmware(*mainfw);
*mainfw = NULL;
}
return -ENOENT;
}
static int ssv6xxx_hci_load_firmware_request(struct ssv6xxx_hci_ctrl *hci_ctrl, u8 *firmware_name)
{
int ret = 0;
const struct firmware *ssv6xxx_fw = NULL;
u8 *fw_buffer = NULL;
u32 sram_addr = FW_START_SRAM_ADDR;
u32 block_count = 0;
u32 block_idx = 0;
u32 res_size;
u8 *fw_data;
u8 interface = HCI_DEVICE_TYPE(hci_ctrl);
#ifdef ENABLE_FW_SELF_CHECK
u32 checksum = FW_CHECKSUM_INIT;
u32 fw_checksum;
u32 retry_count = 3;
u32 *fw_data32;
#else
int writesize = 0;
u32 retry_count = 1;
#endif
//if (hci_ctrl->redownload == 1) {
/* force mcu jump to rom for always*/
// Redownload firmware
HCI_DBG_PRINT(hci_ctrl, "Re-download FW\n");
HCI_JUMP_TO_ROM(hci_ctrl);
//}
// Load firmware
ret = ssv6xxx_hci_get_firmware(hci_ctrl->shi->dev, firmware_name, &ssv6xxx_fw);
if (ret) {
HCI_DBG_PRINT(hci_ctrl, "failed to find firmware (%d)\n", ret);
goto out;
}
// Allocate buffer firmware aligned with FW_BLOCK_SIZE and padding with 0xA5 in empty space.
fw_buffer = (u8 *)kzalloc(FW_BLOCK_SIZE, GFP_KERNEL);
if (fw_buffer == NULL) {
HCI_DBG_PRINT(hci_ctrl, "Failed to allocate buffer for firmware.\n");
goto out;
}
#ifdef ENABLE_FW_SELF_CHECK
block_count = ssv6xxx_fw->size / CHECKSUM_BLOCK_SIZE;
res_size = ssv6xxx_fw->size % CHECKSUM_BLOCK_SIZE;
{
int word_count = (int)(block_count * CHECKSUM_BLOCK_SIZE / sizeof(u32));
int i;
fw_data32 = (u32 *)ssv6xxx_fw->data;
for (i = 0; i < word_count; i++)
checksum += fw_data32[i];
if (res_size) {
memset(fw_buffer, 0xA5, CHECKSUM_BLOCK_SIZE);
memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * CHECKSUM_BLOCK_SIZE], res_size);
// Accumulate checksum for the incomplete block
word_count = (int)(CHECKSUM_BLOCK_SIZE / sizeof(u32));
fw_data32 = (u32 *)fw_buffer;
for (i = 0; i < word_count; i++) {
checksum += fw_data32[i];
}
}
}
// Calculate the final checksum.
checksum = ((checksum >> 24) + (checksum >> 16) + (checksum >> 8) + checksum) & 0x0FF;
checksum <<= 16;
#endif // ENABLE_FW_SELF_CHECK
do {
// Disable MCU (USB ROM code must be alive for downloading FW, so USB doesn't do it)
if (!(interface == SSV_HWIF_INTERFACE_USB)) {
ret = SSV_LOAD_FW_DISABLE_MCU(hci_ctrl);
if (ret == -1)
goto out;
}
// Write firmware to SRAM address 0
#ifdef ENABLE_FW_SELF_CHECK
block_count = ssv6xxx_fw->size / FW_BLOCK_SIZE;
res_size = ssv6xxx_fw->size % FW_BLOCK_SIZE;
HCI_DBG_PRINT(hci_ctrl, "Writing %d blocks to SSV6XXX...", block_count);
for (block_idx = 0, fw_data = (u8 *)ssv6xxx_fw->data, sram_addr = 0;block_idx < block_count;
block_idx++, fw_data += FW_BLOCK_SIZE, sram_addr += FW_BLOCK_SIZE) {
memcpy(fw_buffer, fw_data, FW_BLOCK_SIZE);
if ((ret = HCI_LOAD_FW(hci_ctrl, sram_addr, (u8 *)fw_buffer, FW_BLOCK_SIZE)) != 0)
break;
}
if(res_size) {
memset(fw_buffer, 0xA5, FW_BLOCK_SIZE);
memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * FW_BLOCK_SIZE], res_size);
if ((ret = HCI_LOAD_FW(hci_ctrl, sram_addr, (u8 *)fw_buffer,
((res_size/CHECKSUM_BLOCK_SIZE)+1)*CHECKSUM_BLOCK_SIZE)) != 0)
break;
}
#else // ENABLE_FW_SELF_CHECK
block_count = ssv6xxx_fw->size / FW_BLOCK_SIZE;
res_size = ssv6xxx_fw->size % FW_BLOCK_SIZE;
writesize = sdio_align_size(func,res_size);
HCI_DBG_PRINT(hci_ctrl, "Writing %d blocks to SSV6XXX...", block_count);
for (block_idx = 0, fw_data = (u8 *)ssv6xxx_fw->data, sram_addr = 0;block_idx < block_count;
block_idx++, fw_data += FW_BLOCK_SIZE, sram_addr += FW_BLOCK_SIZE) {
memcpy(fw_buffer, fw_data, FW_BLOCK_SIZE);
if ((ret = HCI_LOAD_FW(hci_ctrl, sram_addr, (u8 *)fw_buffer, FW_BLOCK_SIZE)) != 0)
break;
}
if(res_size) {
memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * FW_BLOCK_SIZE], res_size);
if ((ret = HCI_LOAD_FW(hci_ctrl, sram_addr, (u8 *)fw_buffer, writesize)) != 0)
break;
}
#endif // ENABLE_FW_SELF_CHECK
if (ret == 0) {
// Reset CPU for USB switching ROM to firmware
if (interface == SSV_HWIF_INTERFACE_USB) {
ret = SSV_RESET_CPU(hci_ctrl);
if (ret == -1)
goto out;
}
#ifdef ENABLE_FW_SELF_CHECK
block_count = ssv6xxx_fw->size / CHECKSUM_BLOCK_SIZE;
res_size = ssv6xxx_fw->size % CHECKSUM_BLOCK_SIZE;
if(res_size)
block_count++;
// Inform FW that how many blocks is downloaded such that FW can calculate the checksum.
SSV_LOAD_FW_SET_STATUS(hci_ctrl, (block_count << 16));
#endif // ENABLE_FW_SELF_CHECK
// Release reset to let CPU run.
SSV_LOAD_FW_ENABLE_MCU(hci_ctrl);
HCI_DBG_PRINT(hci_ctrl, "Firmware \"%s\" loaded\n", firmware_name);
#ifdef ENABLE_FW_SELF_CHECK
// Wait FW to calculate checksum.
msleep(50);
// Check checksum result and set to complement value if checksum is OK.
SSV_LOAD_FW_GET_STATUS(hci_ctrl, &fw_checksum);
fw_checksum = fw_checksum & FW_STATUS_MASK;
if (fw_checksum == checksum) {
SSV_LOAD_FW_SET_STATUS(hci_ctrl, (~checksum & FW_STATUS_MASK));
ret = 0;
HCI_DBG_PRINT(hci_ctrl, "Firmware check OK.\n");
break;
} else {
HCI_DBG_PRINT(hci_ctrl, "FW checksum error: %04x != %04x\n", fw_checksum, checksum);
ret = -1;
}
#endif
} else {
HCI_DBG_PRINT(hci_ctrl, "Firmware \"%s\" download failed. (%d)\n", firmware_name, ret);
ret = -1;
}
} while (--retry_count);
if (ret)
goto out;
// hci_ctrl->redownload = 1;
ret = 0;
out:
if (ssv6xxx_fw)
release_firmware(ssv6xxx_fw);
if (fw_buffer != NULL)
kfree(fw_buffer);
return ret;
}
#endif
static int ssv6xxx_hci_load_firmware_fromheader(struct ssv_hw *sh, u8 *firmware_name)
{
return _ssv6xxx_load_firmware(sh, firmware_name, 0);
}
static int ssv6xxx_load_fw(struct ssv_hw *sh, u8 *firmware_name, u8 openfile)
{
int ret = 0;
HAL_LOAD_FW_PRE_CONFIG_DEVICE(sh);
if (openfile)
ret = ssv6xxx_hci_load_firmware_openfile(sh, firmware_name);
else
ret = ssv6xxx_hci_load_firmware_fromheader(sh, firmware_name);
#if 0 //Keep it, but not used.
ret = ssv6xxx_hci_load_firmware_request(hci_ctrl, firmware_name);
#endif
// Sleep to let SSV6XXX get ready.
msleep(50);
if (ret == 0)
HAL_LOAD_FW_POST_CONFIG_DEVICE(sh);
return ret;
}
static void ssv6xxx_attach_common_hal (struct ssv_hal_ops *hal_ops)
{
hal_ops->cmd_cali = ssv6xxx_cmd_cali;
hal_ops->cmd_rc = ssv6xxx_cmd_rc;
hal_ops->cmd_efuse = ssv6xxx_cmd_efuse;
hal_ops->set_sifs = ssv6xxx_set_sifs;
hal_ops->cmd_hwinfo = ssv6xxx_cmd_hwinfo;
hal_ops->get_tkip_mmic_err = ssv6xxx_get_tkip_mmic_err;
hal_ops->cmd_txgen = ssv6xxx_cmd_txgen;
hal_ops->cmd_rf = ssv6xxx_cmd_rf;
hal_ops->cmd_hwq_limit = ssv6xxx_cmd_hwq_limit;
hal_ops->init_gpio_cfg = ssv6xxx_init_gpio_cfg;
hal_ops->flash_read_all_map = ssv6xxx_flash_read_all_map;
hal_ops->write_efuse = ssv6xxx_write_efuse;
hal_ops->update_rf_table = ssv6xxx_update_rf_table;
hal_ops->set_on3_enable = ssv6xxx_set_on3_enable;
hal_ops->init_hci_rx_aggr = ssv6xxx_init_hci_rx_aggr;
hal_ops->reset_hw_mac = ssv6xxx_reset_hw_mac;
hal_ops->set_crystal_clk = ssv6xxx_set_crystal_clk;
hal_ops->wait_usb_rom_ready = ssv6xxx_wait_usb_rom_ready;
hal_ops->detach_usb_hci = ssv6xxx_detach_usb_hci;
hal_ops->pll_chk = ssv6xxx_pll_chk;
hal_ops->put_mic_space_for_hw_ccmp_encrypt = ssv6xxx_put_mic_space_for_hw_ccmp_encrypt;
#ifdef CONFIG_PM
hal_ops->save_clear_trap_reason = ssv6xxx_save_clear_trap_reason;
hal_ops->restore_trap_reason = ssv6xxx_restore_trap_reason;
hal_ops->ps_save_reset_rx_flow = ssv6xxx_ps_save_reset_rx_flow;
hal_ops->ps_restore_rx_flow = ssv6xxx_ps_restore_rx_flow;
hal_ops->pmu_awake = ssv6xxx_pmu_awake;
hal_ops->ps_hold_on3 = ssv6xxx_ps_hold_on3;
#endif
hal_ops->get_sram_mode = ssv6xxx_get_sram_mode;
hal_ops->rc_mac80211_tx_rate_idx = ssv6xxx_rc_mac80211_tx_rate_idx;
hal_ops->update_mac80211_chan_info = ssv6xxx_update_mac80211_chan_info;
hal_ops->read_allid_map = ssv6xxx_read_allid_map;
hal_ops->read_txid_map = ssv6xxx_read_txid_map;
hal_ops->read_rxid_map = ssv6xxx_read_rxid_map;
hal_ops->load_fw = ssv6xxx_load_fw;
}
int ssv6xxx_init_hal(struct ssv_softc *sc)
{
struct ssv_hw *sh;
int ret = 0;
struct ssv_hal_ops *hal_ops = NULL;
extern void ssv_attach_ssv6006(struct ssv_softc *sc, struct ssv_hal_ops *hal_ops);
bool chip_supportted = false;
struct ssv6xxx_platform_data *priv = sc->dev->platform_data;
// alloc hal_ops memory
hal_ops = kzalloc(sizeof(struct ssv_hal_ops), GFP_KERNEL);
if (hal_ops == NULL) {
printk("%s(): Fail to alloc hal_ops\n", __FUNCTION__);
return -ENOMEM;
}
// load common HAL layer function;
ssv6xxx_attach_common_hal(hal_ops);
#ifdef SSV_SUPPORT_SSV6006
if ( strstr(priv->chip_id, SSV6006)
|| strstr(priv->chip_id, SSV6006C)
|| strstr(priv->chip_id, SSV6006D)) {
printk(KERN_INFO"%s\n", sc->platform_dev->id_entry->name);
printk(KERN_INFO"Attach SSV6006 family HAL function \n");
ssv_attach_ssv6006(sc, hal_ops);
chip_supportted = true;
}
#endif
if (!chip_supportted) {
printk(KERN_ERR "Chip \"%s\" is not supported by this driver\n", sc->platform_dev->id_entry->name);
ret = -EINVAL;
goto out;
}
sh = hal_ops->alloc_hw();
if (sh == NULL) {
ret = -ENOMEM;
goto out;
}
memcpy(&sh->hal_ops, hal_ops, sizeof(struct ssv_hal_ops));
sc->sh = sh;
sh->sc = sc;
sh->priv = sc->dev->platform_data;
sh->hci.dev = sc->dev;
sh->hci.if_ops = sh->priv->ops;
sh->hci.skb_alloc = ssv_skb_alloc;
sh->hci.skb_free = ssv_skb_free;
sh->hci.hci_rx_cb = ssv6200_rx;
sh->hci.hci_is_rx_q_full = ssv6200_is_rx_q_full;
sh->priv->skb_alloc = ssv_skb_alloc_ex;
sh->priv->skb_free = ssv_skb_free;
sh->priv->skb_param = sc;
// Set jump to rom functions for HWIF
sh->priv->enable_usb_acc = ssv6xxx_enable_usb_acc;
sh->priv->disable_usb_acc = ssv6xxx_disable_usb_acc;
sh->priv->jump_to_rom = ssv6xxx_jump_to_rom;
sh->priv->usb_param = sc;
sh->priv->rx_mode = ssv6xxx_rx_mode;
sh->priv->rx_mode_param = sc;
sh->hci.sc = sc;
sh->hci.sh = sh;
out:
kfree(hal_ops);
return ret;
}