/** ****************************************************************************** * * rwnx_cmds.c * * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to * LMAC FW * * Copyright (C) RivieraWaves 2014-2019 * ****************************************************************************** */ #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) #include #endif #include #include "aicsdio_txrxif.h" #include "aicsdio.h" #include "aic_bsp_driver.h" #include "md5.h" #include "aic8800dc_compat.h" #include "aic8800d80_compat.h" #include "aicwf_firmware_array.h" #define FW_PATH_MAX 200 extern int adap_test; extern char aic_fw_path[FW_PATH_MAX]; extern struct aic_sdio_dev *aicbsp_sdiodev; static void cmd_dump(const struct rwnx_cmd *cmd) { printk(KERN_CRIT "tkn[%d] flags:%04x result:%3d cmd:%4d - reqcfm(%4d)\n", cmd->tkn, cmd->flags, cmd->result, cmd->id, cmd->reqid); } static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) { //printk("cmdcmp\n"); lockdep_assert_held(&cmd_mgr->lock); list_del(&cmd->list); cmd_mgr->queue_sz--; cmd->flags |= RWNX_CMD_FLAG_DONE; if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) { kfree(cmd); } else { if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) { cmd->result = 0; complete(&cmd->complete); } } } static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) { bool defer_push = false; int err = 0; spin_lock_bh(&cmd_mgr->lock); if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) { printk(KERN_CRIT"cmd queue crashed\n"); cmd->result = -EPIPE; spin_unlock_bh(&cmd_mgr->lock); return -EPIPE; } if (!list_empty(&cmd_mgr->cmds)) { struct rwnx_cmd *last; if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) { printk(KERN_CRIT"Too many cmds (%d) already queued\n", cmd_mgr->max_queue_sz); cmd->result = -ENOMEM; spin_unlock_bh(&cmd_mgr->lock); return -ENOMEM; } last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list); if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH)) { cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH; defer_push = true; } } if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM) cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM; cmd->tkn = cmd_mgr->next_tkn++; cmd->result = -EINTR; if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) init_completion(&cmd->complete); list_add_tail(&cmd->list, &cmd_mgr->cmds); cmd_mgr->queue_sz++; spin_unlock_bh(&cmd_mgr->lock); if (!defer_push) { //printk("queue:id=%x, param_len=%u\n", cmd->a2e_msg->id, cmd->a2e_msg->param_len); rwnx_set_cmd_tx((void *)(cmd_mgr->sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); //rwnx_ipc_msg_push(rwnx_hw, cmd, RWNX_CMD_A2EMSG_LEN(cmd->a2e_msg)); kfree(cmd->a2e_msg); } else { //WAKE_CMD_WORK(cmd_mgr); printk("ERR: never defer push!!!!"); return 0; } if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) { unsigned long tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz); if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) { printk(KERN_CRIT"cmd timed-out\n"); cmd_dump(cmd); spin_lock_bh(&cmd_mgr->lock); cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED; if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) { cmd->result = -ETIMEDOUT; cmd_complete(cmd_mgr, cmd); } spin_unlock_bh(&cmd_mgr->lock); err = -ETIMEDOUT; } else { kfree(cmd); } } else { cmd->result = 0; } return err; } static int cmd_mgr_run_callback(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd, struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) { int res; if (!cb) { return 0; } spin_lock(&cmd_mgr->cb_lock); res = cb(cmd, msg); spin_unlock(&cmd_mgr->cb_lock); return res; } static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) { struct rwnx_cmd *cmd; bool found = false; //printk("cmd->id=%x\n", msg->id); spin_lock(&cmd_mgr->lock); list_for_each_entry(cmd, &cmd_mgr->cmds, list) { if (cmd->reqid == msg->id && (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) { if (!cmd_mgr_run_callback(cmd_mgr, cmd, msg, cb)) { found = true; cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM; if (WARN((msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX), "Unexpect E2A msg len %d > %d\n", msg->param_len, RWNX_CMD_E2AMSG_LEN_MAX)) { msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX; } if (cmd->e2a_msg && msg->param_len) memcpy(cmd->e2a_msg, &msg->param, msg->param_len); if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) cmd_complete(cmd_mgr, cmd); break; } } } spin_unlock(&cmd_mgr->lock); if (!found) cmd_mgr_run_callback(cmd_mgr, NULL, msg, cb); return 0; } static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr) { struct rwnx_cmd *cur; spin_lock_bh(&cmd_mgr->lock); list_for_each_entry(cur, &cmd_mgr->cmds, list) { cmd_dump(cur); } spin_unlock_bh(&cmd_mgr->lock); } static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr) { struct rwnx_cmd *cur, *nxt; spin_lock_bh(&cmd_mgr->lock); list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) { list_del(&cur->list); cmd_mgr->queue_sz--; if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK)) complete(&cur->complete); } spin_unlock_bh(&cmd_mgr->lock); } void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr) { cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED; INIT_LIST_HEAD(&cmd_mgr->cmds); cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED; spin_lock_init(&cmd_mgr->lock); spin_lock_init(&cmd_mgr->cb_lock); cmd_mgr->queue = &cmd_mgr_queue; cmd_mgr->print = &cmd_mgr_print; cmd_mgr->drain = &cmd_mgr_drain; cmd_mgr->llind = NULL;//&cmd_mgr_llind; cmd_mgr->msgind = &cmd_mgr_msgind; #if 0 INIT_WORK(&cmd_mgr->cmdWork, cmd_mgr_task_process); cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq"); if (!cmd_mgr->cmd_wq) { txrx_err("insufficient memory to create cmd workqueue.\n"); return; } #endif } void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr) { cmd_mgr->print(cmd_mgr); cmd_mgr->drain(cmd_mgr); cmd_mgr->print(cmd_mgr); memset(cmd_mgr, 0, sizeof(*cmd_mgr)); } void rwnx_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len) { struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev; struct aicwf_bus *bus = sdiodev->bus_if; u8 *buffer = bus->cmd_buf; u16 index = 0; memset(buffer, 0, CMD_BUF_MAX); buffer[0] = (len+4) & 0x00ff; buffer[1] = ((len+4) >> 8) &0x0f; buffer[2] = 0x11; if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW) buffer[3] = 0x0; else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) buffer[3] = crc8_ponl_107(&buffer[0], 3); // crc8 index += 4; //there is a dummy word index += 4; //make sure little endian put_u16(&buffer[index], msg->id); index += 2; put_u16(&buffer[index], msg->dest_id); index += 2; put_u16(&buffer[index], msg->src_id); index += 2; put_u16(&buffer[index], msg->param_len); index += 2; memcpy(&buffer[index], (u8 *)msg->param, msg->param_len); aicwf_bus_txmsg(bus, buffer, len + 8); } static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id, lmac_task_id_t const dest_id, lmac_task_id_t const src_id, uint16_t const param_len) { struct lmac_msg *msg; gfp_t flags; if (in_softirq()) flags = GFP_ATOMIC; else flags = GFP_KERNEL; msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len, flags); if (msg == NULL) { printk(KERN_CRIT "%s: msg allocation failed\n", __func__); return NULL; } msg->id = id; msg->dest_id = dest_id; msg->src_id = src_id; msg->param_len = param_len; return msg->param; } static void rwnx_msg_free(struct lmac_msg *msg, const void *msg_params) { kfree(msg); } static int rwnx_send_msg(struct aic_sdio_dev *sdiodev, const void *msg_params, int reqcfm, lmac_msg_id_t reqid, void *cfm) { struct lmac_msg *msg; struct rwnx_cmd *cmd; bool nonblock; int ret = 0; msg = container_of((void *)msg_params, struct lmac_msg, param); if (sdiodev->bus_if->state == BUS_DOWN_ST) { rwnx_msg_free(msg, msg_params); printk("bus is down\n"); return 0; } nonblock = 0; cmd = kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL); cmd->result = -EINTR; cmd->id = msg->id; cmd->reqid = reqid; cmd->a2e_msg = msg; cmd->e2a_msg = cfm; if (nonblock) cmd->flags = RWNX_CMD_FLAG_NONBLOCK; if (reqcfm) cmd->flags |= RWNX_CMD_FLAG_REQ_CFM; if (reqcfm) { cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more ret = sdiodev->cmd_mgr.queue(&sdiodev->cmd_mgr, cmd); } else { rwnx_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); } if (!reqcfm) kfree(cmd); return ret; } int rwnx_send_dbg_mem_block_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, u32 mem_size, u32 *mem_data) { struct dbg_mem_block_write_req *mem_blk_write_req; /* Build the DBG_MEM_BLOCK_WRITE_REQ message */ mem_blk_write_req = rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, sizeof(struct dbg_mem_block_write_req)); if (!mem_blk_write_req) return -ENOMEM; /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */ mem_blk_write_req->memaddr = mem_addr; mem_blk_write_req->memsize = mem_size; memcpy(mem_blk_write_req->memdata, mem_data, mem_size); /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */ return rwnx_send_msg(sdiodev, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, NULL); } int rwnx_send_dbg_mem_read_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, struct dbg_mem_read_cfm *cfm) { struct dbg_mem_read_req *mem_read_req; /* Build the DBG_MEM_READ_REQ message */ mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID, sizeof(struct dbg_mem_read_req)); if (!mem_read_req) return -ENOMEM; /* Set parameters for the DBG_MEM_READ_REQ message */ mem_read_req->memaddr = mem_addr; /* Send the DBG_MEM_READ_REQ message to LMAC FW */ return rwnx_send_msg(sdiodev, mem_read_req, 1, DBG_MEM_READ_CFM, cfm); } int rwnx_send_dbg_mem_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, u32 mem_data) { struct dbg_mem_write_req *mem_write_req; /* Build the DBG_MEM_WRITE_REQ message */ mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID, sizeof(struct dbg_mem_write_req)); if (!mem_write_req) return -ENOMEM; /* Set parameters for the DBG_MEM_WRITE_REQ message */ mem_write_req->memaddr = mem_addr; mem_write_req->memdata = mem_data; /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */ return rwnx_send_msg(sdiodev, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL); } int rwnx_send_dbg_mem_mask_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, u32 mem_mask, u32 mem_data) { struct dbg_mem_mask_write_req *mem_mask_write_req; /* Build the DBG_MEM_MASK_WRITE_REQ message */ mem_mask_write_req = rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, sizeof(struct dbg_mem_mask_write_req)); if (!mem_mask_write_req) return -ENOMEM; /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */ mem_mask_write_req->memaddr = mem_addr; mem_mask_write_req->memmask = mem_mask; mem_mask_write_req->memdata = mem_data; /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */ return rwnx_send_msg(sdiodev, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, NULL); } int rwnx_send_dbg_start_app_req(struct aic_sdio_dev *sdiodev, u32 boot_addr, u32 boot_type, struct dbg_start_app_cfm *start_app_cfm) { struct dbg_start_app_req *start_app_req; /* Build the DBG_START_APP_REQ message */ start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID, sizeof(struct dbg_start_app_req)); if (!start_app_req) { printk("start app nomen\n"); return -ENOMEM; } /* Set parameters for the DBG_START_APP_REQ message */ start_app_req->bootaddr = boot_addr; start_app_req->boottype = boot_type; /* Send the DBG_START_APP_REQ message to LMAC FW */ return rwnx_send_msg(sdiodev, start_app_req, 1, DBG_START_APP_CFM, start_app_cfm); } static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = { }; static msg_cb_fct *msg_hdlrs[] = { [TASK_DBG] = dbg_hdlrs, }; void rwnx_rx_handle_msg(struct aic_sdio_dev *sdiodev, struct ipc_e2a_msg *msg) { sdiodev->cmd_mgr.msgind(&sdiodev->cmd_mgr, msg, msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); #endif #define MD5(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10],x[11],x[12],x[13],x[14],x[15] #define MD5PINRT "file md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n" int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device) { #ifdef CONFIG_USE_FW_REQUEST const struct firmware *fw = NULL; u32 *dst = NULL; void *buffer=NULL; MD5_CTX md5; unsigned char decrypt[16]; int size = 0; int ret = 0; printk("%s: request firmware = %s \n", __func__ ,name); ret = request_firmware(&fw, name, NULL); if (ret < 0) { printk("Load %s fail\n", name); release_firmware(fw); return -1; } size = fw->size; dst = (u32 *)fw->data; if (size <= 0) { printk("wrong size of firmware file\n"); release_firmware(fw); return -1; } buffer = vmalloc(size); memset(buffer, 0, size); memcpy(buffer, dst, size); *fw_buf = buffer; MD5Init(&md5); MD5Update(&md5, (unsigned char *)buffer, size); MD5Final(&md5, decrypt); printk(MD5PINRT, MD5(decrypt)); release_firmware(fw); return size; #else void *buffer = NULL; char *path = NULL; struct file *fp = NULL; int size = 0, len = 0, i = 0; ssize_t rdlen = 0; u32 *src = NULL, *dst = NULL; MD5_CTX md5; unsigned char decrypt[16]; #ifdef CONFIG_FIRMWARE_ARRAY size = aicwf_get_firmware_array((char*)name, fw_buf); printk("%s size:%d \r\n", __func__, size); MD5Init(&md5); MD5Update(&md5, (unsigned char *)*fw_buf, size); MD5Final(&md5, decrypt); printk(MD5PINRT, MD5(decrypt)); return size; #endif /* get the firmware path */ path = __getname(); if (!path) { *fw_buf = NULL; return -1; } if(strlen(aic_fw_path) > 0){ len = snprintf(path, AICBSP_FW_PATH_MAX, "%s/%s", aic_fw_path, name); }else{ len = snprintf(path, AICBSP_FW_PATH_MAX, "%s/%s", AICBSP_FW_PATH, name); } if (len >= AICBSP_FW_PATH_MAX) { printk("%s: %s file's path too long\n", __func__, name); *fw_buf = NULL; __putname(path); return -1; } printk("%s :firmware path = %s \n", __func__, path); /* open the firmware file */ fp = filp_open(path, O_RDONLY, 0); if (IS_ERR_OR_NULL(fp)) { printk("%s: %s file failed to open\n", __func__, name); *fw_buf = NULL; __putname(path); fp = NULL; return -1; } size = i_size_read(file_inode(fp)); if (size <= 0) { printk("%s: %s file size invalid %d\n", __func__, name, size); *fw_buf = NULL; __putname(path); filp_close(fp, NULL); fp = NULL; return -1; } /* start to read from firmware file */ buffer = vmalloc(size); if (!buffer) { *fw_buf = NULL; __putname(path); filp_close(fp, NULL); fp = NULL; return -1; }else{ memset(buffer, 0, size); } #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16) rdlen = kernel_read(fp, buffer, size, &fp->f_pos); #else rdlen = kernel_read(fp, fp->f_pos, buffer, size); #endif if (size != rdlen) { printk("%s: %s file rdlen invalid %ld\n", __func__, name, (long int)rdlen); *fw_buf = NULL; __putname(path); filp_close(fp, NULL); fp = NULL; vfree(buffer); buffer = NULL; return -1; } if (rdlen > 0) { fp->f_pos += rdlen; } /*start to transform the data format*/ src = (u32 *)buffer; dst = (u32 *)vmalloc(size); if (!dst) { *fw_buf = NULL; __putname(path); filp_close(fp, NULL); fp = NULL; vfree(buffer); buffer = NULL; return -1; }else{ memset(dst, 0, size); } for (i = 0; i < (size/4); i++) { dst[i] = src[i]; } __putname(path); filp_close(fp, NULL); fp = NULL; vfree(buffer); buffer = NULL; *fw_buf = dst; MD5Init(&md5); MD5Update(&md5, (unsigned char *)dst, size); MD5Final(&md5, decrypt); printk(MD5PINRT, MD5(decrypt)); return size; #endif } int aicwf_patch_table_load(struct aic_sdio_dev *rwnx_hw, char *filename) { struct device *dev = rwnx_hw->dev; int err = 0; unsigned int i = 0, size; u32 *dst = NULL; u8 *describle; u32 fmacfw_patch_tbl_8800dc_u02_describe_size = 124; u32 fmacfw_patch_tbl_8800dc_u02_describe_base;//read from patch_tbl /* Copy the file on the Embedded side */ printk("### Upload %s \n", filename); size = rwnx_load_firmware(&dst, filename,dev); if (!dst) { printk("No such file or directory\n"); return -1; } if (size <= 0) { printk("wrong size of firmware file\n"); dst = NULL; err = -1; } printk("tbl size = %d \n",size); fmacfw_patch_tbl_8800dc_u02_describe_base = dst[0]; AICWFDBG(LOGINFO, "FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE = %x \n",fmacfw_patch_tbl_8800dc_u02_describe_base); if (!err && (i < size)) { err=rwnx_send_dbg_mem_block_write_req(rwnx_hw, fmacfw_patch_tbl_8800dc_u02_describe_base, fmacfw_patch_tbl_8800dc_u02_describe_size + 4, dst); if(err){ printk("write describe information fail \n"); } describle=kzalloc(fmacfw_patch_tbl_8800dc_u02_describe_size,GFP_KERNEL); memcpy(describle,&dst[1],fmacfw_patch_tbl_8800dc_u02_describe_size); printk("%s",describle); kfree(describle); describle=NULL; } if (!err && (i < size)) { for (i =(128/4); i < (size/4); i +=2) { printk("patch_tbl: %x %x\n", dst[i], dst[i+1]); err = rwnx_send_dbg_mem_write_req(rwnx_hw, dst[i], dst[i+1]); } if (err) { printk("bin upload fail: %x, err:%d\r\n", dst[i], err); } } if (dst) { #ifndef CONFIG_FIRMWARE_ARRAY vfree(dst); #endif dst = NULL; } return err; } extern char aic_fw_path[200]; extern int testmode; int aicwf_plat_patch_load_8800dc(struct aic_sdio_dev *sdiodev) { int ret = 0; if (testmode == 0) { #if !defined(CONFIG_FPGA_VERIFICATION) if (chip_sub_id == 0) { printk("u01 is loaing ###############\n"); ret = rwnx_plat_bin_fw_upload_android(sdiodev, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_8800DC); } else if (chip_sub_id == 1) { printk("u02 is loaing ###############\n"); ret = rwnx_plat_bin_fw_upload_android(sdiodev, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_8800DC_U02); } else { printk("unsupported id: %d\n", chip_sub_id); } #endif } else { if (chip_sub_id == 0) { ret = rwnx_plat_bin_fw_upload_android(sdiodev, ROM_FMAC_PATCH_ADDR, RWNX_MAC_RF_PATCH_NAME_8800DC); } if (!ret) { ret = rwnx_plat_bin_fw_upload_android(sdiodev, RAM_LMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME_8800DC); } } return ret; } static int rwnx_plat_patch_load(struct aic_sdio_dev *sdiodev) { int ret = 0; RWNX_DBG(RWNX_FN_ENTRY_STR); if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){ #if !defined(CONFIG_DPD) aicwf_misc_ram_init_8800dc(sdiodev); #endif printk("rwnx_plat_patch_loading\n"); ret = aicwf_plat_patch_load_8800dc(sdiodev); } return ret; } int rwnx_plat_bin_fw_upload_android(struct aic_sdio_dev *sdiodev, u32 fw_addr, const char *filename) { struct device *dev = sdiodev->dev; unsigned int i = 0; int size; u32 *dst = NULL; int err = 0; printk("%s\n",__func__); /* load aic firmware */ size = rwnx_load_firmware(&dst, filename, dev); if (size <= 0) { printk("wrong size of firmware file\n"); #ifndef CONFIG_FIRMWARE_ARRAY vfree(dst); #endif dst = NULL; return -1; } /* Copy the file on the Embedded side */ if (size > 1024) {// > 1KB data for (i = 0; i < (size - 1024); i += 1024) {//each time write 1KB err = rwnx_send_dbg_mem_block_write_req(sdiodev, fw_addr + i, 1024, dst + i / 4); if (err) { printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err); break; } } } if (!err && (i < size)) {// <1KB data err = rwnx_send_dbg_mem_block_write_req(sdiodev, fw_addr + i, size - i, dst + i / 4); if (err) { printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err); } } if (dst) { #ifndef CONFIG_FIRMWARE_ARRAY vfree(dst); #endif dst = NULL; } return err; } int aicbt_patch_table_free(struct aicbt_patch_table **head) { struct aicbt_patch_table *p = *head, *n = NULL; while (p) { n = p->next; vfree(p->name); vfree(p->data); vfree(p); p = n; } *head = NULL; return 0; } struct aicbt_patch_table *aicbt_patch_table_alloc(const char *filename) { uint8_t *rawdata = NULL, *p; int size; struct aicbt_patch_table *head = NULL, *new = NULL, *cur = NULL; /* load aic firmware */ size = rwnx_load_firmware((u32 **)&rawdata, filename, NULL); if (size <= 0) { printk("wrong size of firmware file\n"); goto err; } p = rawdata; if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) { printk("TAG err\n"); goto err; } p += 16; while (p - rawdata < size) { new = (struct aicbt_patch_table *)vmalloc(sizeof(struct aicbt_patch_table)); memset(new, 0, sizeof(struct aicbt_patch_table)); if (head == NULL) { head = new; cur = new; } else { cur->next = new; cur = cur->next; } cur->name = (char *)vmalloc(sizeof(char) * 16); memset(cur->name, 0, sizeof(char) * 16); memcpy(cur->name, p, 16); p += 16; cur->type = *(uint32_t *)p; p += 4; cur->len = *(uint32_t *)p; p += 4; if((cur->type ) >= 1000 ) {//Temp Workaround cur->len = 0; }else{ if(cur->len > 0){ cur->data = (uint32_t *)vmalloc(sizeof(uint8_t) * cur->len * 8); memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8); memcpy(cur->data, p, cur->len * 8); p += cur->len * 8; } } } #ifndef CONFIG_FIRMWARE_ARRAY vfree(rawdata); #endif return head; err: aicbt_patch_table_free(&head); if (rawdata) vfree(rawdata); return NULL; } int aicbt_patch_info_unpack(struct aicbt_patch_info_t *patch_info, struct aicbt_patch_table *head_t) { if (AICBT_PT_INF == head_t->type) { patch_info->info_len = head_t->len; if(patch_info->info_len == 0) return 0; memcpy(&patch_info->adid_addrinf, head_t->data, patch_info->info_len * sizeof(uint32_t) * 2); } return 0; } int aicbt_patch_trap_data_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head) { struct aicbt_patch_info_t patch_info = { .info_len = 0, .adid_addrinf = 0, .addr_adid = 0, .patch_addrinf = 0, .addr_patch = 0, .reset_addr = 0, .reset_val = 0, .adid_flag_addr = 0, .adid_flag = 0, }; if(head == NULL){ return -1; } if(sdiodev->chipid == PRODUCT_ID_AIC8801){ patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR; patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR; } else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC){ if(aicbsp_info.chip_rev == CHIP_REV_U01){ patch_info.addr_adid = RAM_8800DC_U01_ADID_ADDR; }else if(aicbsp_info.chip_rev == CHIP_REV_U02){ patch_info.addr_adid = RAM_8800DC_U02_ADID_ADDR; } patch_info.addr_patch = RAM_8800DC_FW_PATCH_ADDR; aicbt_patch_info_unpack(&patch_info, head); if(patch_info.reset_addr == 0) { patch_info.reset_addr = FW_RESET_START_ADDR; patch_info.reset_val = FW_RESET_START_VAL; patch_info.adid_flag_addr = FW_ADID_FLAG_ADDR; patch_info.adid_flag = FW_ADID_FLAG_VAL; if (rwnx_send_dbg_mem_write_req(sdiodev, patch_info.reset_addr, patch_info.reset_val)) return -1; if (rwnx_send_dbg_mem_write_req(sdiodev, patch_info.adid_flag_addr, patch_info.adid_flag)) return -1; } } else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){ if (aicbsp_info.chip_rev == CHIP_REV_U01) { patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80; patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80; } else if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03) { patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80_U02; patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80_U02; } aicbt_patch_info_unpack(&patch_info, head); if(patch_info.info_len == 0) { printk("%s, aicbt_patch_info_unpack fail\n", __func__); return -1; } } if (rwnx_plat_bin_fw_upload_android(sdiodev, patch_info.addr_adid, aicbsp_firmware_list[aicbsp_info.cpmode].bt_adid)) return -1; if (rwnx_plat_bin_fw_upload_android(sdiodev, patch_info.addr_patch, aicbsp_firmware_list[aicbsp_info.cpmode].bt_patch)) return -1; return 0; } static struct aicbt_info_t aicbt_info = { .btmode = AICBT_BTMODE_DEFAULT, .btport = AICBT_BTPORT_DEFAULT, .uart_baud = AICBT_UART_BAUD_DEFAULT, .uart_flowctrl = AICBT_UART_FC_DEFAULT, .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT, }; struct aicbt_info_t aicbt_info_8800dc = { .btmode = AICBT_BTMODE_BT_WIFI_COMBO, .btport = AICBT_BTPORT_DEFAULT, .uart_baud = AICBT_UART_BAUD_DEFAULT, .uart_flowctrl = AICBT_UART_FC_DEFAULT, .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, .txpwr_lvl = AICBT_TXPWR_LVL_8800dc, }; int aicbt_patch_table_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head) { struct aicbt_patch_table *p; int ret = 0, i; uint32_t *data = NULL; if(head == NULL){ return -1; } if(sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800D80){ if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) { //aicbt_info.btmode = AICBT_BTMODE_BT_ONLY_COANT; aicbt_info.txpwr_lvl = AICBT_TXPWR_LVL_8800d80; } for (p = head; p != NULL; p = p->next) { data = p->data; if (AICBT_PT_BTMODE == p->type) { *(data + 1) = aicbsp_info.hwinfo < 0; *(data + 3) = aicbsp_info.hwinfo; *(data + 5) = aicbsp_info.cpmode; *(data + 7) = aicbt_info.btmode; *(data + 9) = aicbt_info.btport; *(data + 11) = aicbt_info.uart_baud; *(data + 13) = aicbt_info.uart_flowctrl; *(data + 15) = aicbt_info.lpm_enable; *(data + 17) = aicbt_info.txpwr_lvl; printk("%s bt btmode:%d \r\n", __func__, aicbt_info.btmode); printk("%s bt uart_baud:%d \r\n", __func__, aicbt_info.uart_baud); printk("%s bt uart_flowctrl:%d \r\n", __func__, aicbt_info.uart_flowctrl); printk("%s bt lpm_enable:%d \r\n", __func__, aicbt_info.lpm_enable); printk("%s bt tx_pwr:%d \r\n", __func__, aicbt_info.txpwr_lvl); } if (AICBT_PT_VER == p->type) { printk("aicbsp: bt patch version: %s\n", (char *)p->data); continue; } for (i = 0; i < p->len; i++) { ret = rwnx_send_dbg_mem_write_req(sdiodev, *data, *(data + 1)); if (ret != 0) return ret; data += 2; } if (p->type == AICBT_PT_PWRON) udelay(500); } } else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC){ for (p = head; p != NULL; p = p->next) { data = p->data; if (AICBT_PT_BTMODE == p->type) { *(data + 1) = aicbsp_info.hwinfo < 0; *(data + 3) = aicbsp_info.hwinfo; *(data + 5) = aicbsp_info.cpmode; *(data + 7) = aicbt_info_8800dc.btmode; *(data + 9) = aicbt_info_8800dc.btport; *(data + 11) = aicbt_info_8800dc.uart_baud; *(data + 13) = aicbt_info_8800dc.uart_flowctrl; *(data + 15) = aicbt_info_8800dc.lpm_enable; *(data + 17) = aicbt_info_8800dc.txpwr_lvl; printk("%s bt uart_baud:%d \r\n", __func__, aicbt_info_8800dc.uart_baud); printk("%s bt uart_flowctrl:%d \r\n", __func__, aicbt_info_8800dc.uart_flowctrl); printk("%s bt lpm_enable:%d \r\n", __func__, aicbt_info_8800dc.lpm_enable); printk("%s bt tx_pwr:%d \r\n", __func__, aicbt_info_8800dc.txpwr_lvl); } if (AICBT_PT_INF == p->type) { continue; } printk("########## p->type = %d \n",p->type); printk("AICBT_PT_VER = %d \n",AICBT_PT_VER); if (AICBT_PT_VER == p->type) { printk("aicbsp: bt patch version: %s\n", (char *)p->data); continue; } for (i = 0; i < p->len; i++) { ret = rwnx_send_dbg_mem_write_req(sdiodev, *data, *(data + 1)); if (ret != 0) return ret; data += 2; } if (p->type == AICBT_PT_PWRON) udelay(500); } } ///aicbt_patch_table_free(&head); return 0; } int aicbt_init(struct aic_sdio_dev *sdiodev) { int ret = 0; struct aicbt_patch_table *head = aicbt_patch_table_alloc(aicbsp_firmware_list[aicbsp_info.cpmode].bt_table); if (head == NULL){ printk("aicbt_patch_table_alloc fail\n"); return -1; } if (aicbt_patch_trap_data_load(sdiodev, head)) { printk("aicbt_patch_trap_data_load fail\n"); ret = -1; goto err; } if (aicbt_patch_table_load(sdiodev, head)) { printk("aicbt_patch_table_load fail\n"); ret = -1; goto err; } err: aicbt_patch_table_free(&head); return ret; } static int aicwifi_start_from_bootrom(struct aic_sdio_dev *sdiodev) { int ret = 0; /* memory access */ const u32 fw_addr = RAM_FMAC_FW_ADDR; struct dbg_start_app_cfm start_app_cfm; /* fw start */ ret = rwnx_send_dbg_start_app_req(sdiodev, fw_addr, HOST_START_APP_AUTO, &start_app_cfm); if (ret) { return -1; } aicbsp_info.hwinfo_r = start_app_cfm.bootstatus & 0xFF; return 0; } static int start_from_bootrom_8800DC(struct aic_sdio_dev *sdiodev) { int ret = 0; u32 rd_addr; u32 fw_addr; u32 boot_type; struct dbg_mem_read_cfm rd_cfm; /* memory access */ if(testmode == 1){ rd_addr = RAM_LMAC_FW_ADDR; fw_addr = RAM_LMAC_FW_ADDR; } else{ rd_addr = RAM_FMAC_FW_ADDR; fw_addr = RAM_FMAC_FW_ADDR; } AICWFDBG(LOGINFO, "Read FW mem: %08x\n", rd_addr); if ((ret = rwnx_send_dbg_mem_read_req(sdiodev, rd_addr, &rd_cfm))) { return -1; } AICWFDBG(LOGINFO, "cfm: [%08x] = %08x\n", rd_cfm.memaddr, rd_cfm.memdata); if (testmode == 0) { boot_type = HOST_START_APP_DUMMY; } else { boot_type = HOST_START_APP_AUTO; } /* fw start */ AICWFDBG(LOGINFO, "Start app: %08x, %d\n", fw_addr, boot_type); if ((ret = rwnx_send_dbg_start_app_req(sdiodev, fw_addr, boot_type ,NULL))) { return -1; } return 0; } u32 adaptivity_patch_tbl[][2] = { {0x0004, 0x0000320A}, //linkloss_thd {0x0094, 0x00000000}, //ac_param_conf {0x00F8, 0x00010138}, //tx_adaptivity_en }; u32 patch_tbl[][2] = { #if !defined(CONFIG_LINK_DET_5G) {0x0104, 0x00000000}, //link_det_5g #endif #if defined(CONFIG_MCU_MESSAGE) {0x004c, 0x0000004B}, //pkt_cnt_1724=0x4B {0x0050, 0x0011FC00}, //ipc_base_addr #endif }; u32 syscfg_tbl_masked[][3] = { {0x40506024, 0x000000FF, 0x000000DF}, // for clk gate lp_level }; u32 rf_tbl_masked[][3] = { {0x40344058, 0x00800000, 0x00000000},// pll trx }; static int aicwifi_sys_config(struct aic_sdio_dev *sdiodev) { int ret, cnt; int syscfg_num = sizeof(syscfg_tbl_masked) / sizeof(u32) / 3; for (cnt = 0; cnt < syscfg_num; cnt++) { ret = rwnx_send_dbg_mem_mask_write_req(sdiodev, syscfg_tbl_masked[cnt][0], syscfg_tbl_masked[cnt][1], syscfg_tbl_masked[cnt][2]); if (ret) { printk("%x mask write fail: %d\n", syscfg_tbl_masked[cnt][0], ret); return ret; } } ret = rwnx_send_dbg_mem_mask_write_req(sdiodev, rf_tbl_masked[0][0], rf_tbl_masked[0][1], rf_tbl_masked[0][2]); if (ret) { printk("rf config %x write fail: %d\n", rf_tbl_masked[0][0], ret); return ret; } return 0; } static int aicwifi_patch_config(struct aic_sdio_dev *sdiodev) { const u32 rd_patch_addr = RAM_FMAC_FW_ADDR + 0x0180; u32 config_base; uint32_t start_addr = 0x1e6000; u32 patch_addr = start_addr; u32 patch_num = sizeof(patch_tbl)/4; struct dbg_mem_read_cfm rd_patch_addr_cfm; u32 patch_addr_reg = 0x1e5318; u32 patch_num_reg = 0x1e531c; int ret = 0; u16 cnt = 0; int tmp_cnt = 0; int adap_patch_num = 0; if (aicbsp_info.cpmode == AICBSP_CPMODE_TEST) { patch_addr_reg = 0x1e5304; patch_num_reg = 0x1e5308; } ret = rwnx_send_dbg_mem_read_req(sdiodev, rd_patch_addr, &rd_patch_addr_cfm); if (ret) { printk("patch rd fail\n"); return ret; } config_base = rd_patch_addr_cfm.memdata; ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_addr_reg, patch_addr); if (ret) { printk("0x%x write fail\n", patch_addr_reg); return ret; } if(adap_test){ printk("%s for adaptivity test \r\n", __func__); adap_patch_num = sizeof(adaptivity_patch_tbl)/4; ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_num_reg, patch_num + adap_patch_num); }else{ ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_num_reg, patch_num); } if (ret) { printk("0x%x write fail\n", patch_num_reg); return ret; } for (cnt = 0; cnt < patch_num/2; cnt += 1) { ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr+8*cnt, patch_tbl[cnt][0]+config_base); if (ret) { printk("%x write fail\n", start_addr+8*cnt); return ret; } ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr+8*cnt+4, patch_tbl[cnt][1]); if (ret) { printk("%x write fail\n", start_addr+8*cnt+4); return ret; } } tmp_cnt = cnt; if(adap_test){ for(cnt = 0; cnt < adap_patch_num/2; cnt+=1) { if((ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr+8*(cnt+tmp_cnt), adaptivity_patch_tbl[cnt][0]+config_base))) { printk("%x write fail\n", start_addr+8*cnt); } if((ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr+8*(cnt+tmp_cnt)+4, adaptivity_patch_tbl[cnt][1]))) { printk("%x write fail\n", start_addr+8*cnt+4); } } } return 0; } int aicwifi_init(struct aic_sdio_dev *sdiodev) { if(sdiodev->chipid == PRODUCT_ID_AIC8801){ if (rwnx_plat_bin_fw_upload_android(sdiodev, RAM_FMAC_FW_ADDR, aicbsp_firmware_list[aicbsp_info.cpmode].wl_fw)) { printk("download wifi fw fail\n"); return -1; } if (aicwifi_patch_config(sdiodev)) { printk("aicwifi_patch_config fail\n"); return -1; } if (aicwifi_sys_config(sdiodev)) { printk("aicwifi_sys_config fail\n"); return -1; } if (aicwifi_start_from_bootrom(sdiodev)) { printk("wifi start fail\n"); return -1; } }else if (sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){ printk("############ aicwifi_init begin \n"); system_config_8800dc(sdiodev); printk("############ system_config_8800dc done\n"); rwnx_plat_patch_load(sdiodev); printk("############ rwnx_plat_patch_load done\n"); //rwnx_plat_userconfig_load(sdiodev); aicwf_patch_config_8800dc(sdiodev); printk("############ aicwf_patch_config_8800dc done\n"); start_from_bootrom_8800DC(sdiodev); }else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){ if (rwnx_plat_bin_fw_upload_android(sdiodev, RAM_FMAC_FW_ADDR, aicbsp_firmware_list[aicbsp_info.cpmode].wl_fw)) { printk("8800d80 download wifi fw fail\n"); return -1; } if (aicwifi_patch_config_8800d80(sdiodev)) { printk("aicwifi_patch_config_8800d80 fail\n"); return -1; } if (aicwifi_sys_config_8800d80(sdiodev)) { printk("aicwifi_patch_config_8800d80 fail\n"); return -1; } if (aicwifi_start_from_bootrom(sdiodev)) { printk("8800d80 wifi start fail\n"); return -1; } } #ifdef CONFIG_GPIO_WAKEUP if (aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 4)) { sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg); return -1; } #endif return 0; } u32 aicbsp_syscfg_tbl[][2] = { {0x40500014, 0x00000101}, // 1) {0x40500018, 0x00000109}, // 2) {0x40500004, 0x00000010}, // 3) the order should not be changed // def CONFIG_PMIC_SETTING // U02 bootrom only {0x40040000, 0x00001AC8}, // 1) fix panic {0x40040084, 0x00011580}, {0x40040080, 0x00000001}, {0x40100058, 0x00000000}, {0x50000000, 0x03220204}, // 2) pmic interface init {0x50019150, 0x00000002}, // 3) for 26m xtal, set div1 {0x50017008, 0x00000000}, // 4) stop wdg }; static int aicbsp_system_config(struct aic_sdio_dev *sdiodev) { int syscfg_num = sizeof(aicbsp_syscfg_tbl) / sizeof(u32) / 2; int ret, cnt; for (cnt = 0; cnt < syscfg_num; cnt++) { ret = rwnx_send_dbg_mem_write_req(sdiodev, aicbsp_syscfg_tbl[cnt][0], aicbsp_syscfg_tbl[cnt][1]); if (ret) { sdio_err("%x write fail: %d\n", aicbsp_syscfg_tbl[cnt][0], ret); return ret; } } return 0; } int aicbsp_platform_init(struct aic_sdio_dev *sdiodev) { rwnx_cmd_mgr_init(&sdiodev->cmd_mgr); sdiodev->cmd_mgr.sdiodev = (void *)sdiodev; return 0; } void aicbsp_platform_deinit(struct aic_sdio_dev *sdiodev) { (void)sdiodev; } int aicbsp_driver_fw_init(struct aic_sdio_dev *sdiodev) { u32 mem_addr; struct dbg_mem_read_cfm rd_mem_addr_cfm; u32 btenable = 0; mem_addr = 0x40500000; testmode = aicbsp_info.cpmode; if(sdiodev->chipid == PRODUCT_ID_AIC8801){ if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) return -1; aicbsp_info.chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16); btenable = 1; if (aicbsp_info.chip_rev != CHIP_REV_U02 && aicbsp_info.chip_rev != CHIP_REV_U03 && aicbsp_info.chip_rev != CHIP_REV_U04) { pr_err("aicbsp: %s, unsupport chip rev: %d\n", __func__, aicbsp_info.chip_rev); return -1; } if (aicbsp_info.chip_rev != CHIP_REV_U02) aicbsp_firmware_list = fw_u03; if (aicbsp_system_config(sdiodev)) return -1; } else if (sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){ if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) return -1; aicbsp_info.chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16); btenable = ((rd_mem_addr_cfm.memdata >> 26) & 0x1); AICWFDBG(LOGINFO, "btenable = %d \n",btenable); if(btenable == 0){ sdiodev->chipid = PRODUCT_ID_AIC8800DW; AICWFDBG(LOGINFO, "AIC8800DC change to AIC8800DW \n"); } if (aicbsp_info.chip_rev != CHIP_REV_U01 && aicbsp_info.chip_rev != CHIP_REV_U02 && aicbsp_info.chip_rev != CHIP_REV_U03 && aicbsp_info.chip_rev != CHIP_REV_U04) { pr_err("aicbsp: %s, unsupport chip rev: %d\n", __func__, aicbsp_info.chip_rev); return -1; } if(aicbsp_info.chip_rev == CHIP_REV_U01){ aicbsp_firmware_list = fw_8800dc_u01; }else{ aicbsp_firmware_list = fw_8800dc_u02; } } else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){ if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) return -1; aicbsp_info.chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16); btenable = 1; if (aicbsp_info.chip_rev == CHIP_REV_U01) aicbsp_firmware_list = fw_8800d80_u01; if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03) aicbsp_firmware_list = fw_8800d80_u02; if (aicbsp_system_config_8800d80(sdiodev)) return -1; } AICWFDBG(LOGINFO, "aicbsp: %s, chip rev: %d\n", __func__, aicbsp_info.chip_rev); #ifndef CONFIG_MCU_MESSAGE if(btenable == 1){ if (aicbt_init(sdiodev)) return -1; } #endif if (aicwifi_init(sdiodev)) return -1; return 0; } int aicbsp_get_feature(struct aicbsp_feature_t *feature, char *fw_path) { if (aicbsp_sdiodev->chipid == PRODUCT_ID_AIC8801 || aicbsp_sdiodev->chipid == PRODUCT_ID_AIC8800DC || aicbsp_sdiodev->chipid == PRODUCT_ID_AIC8800DW){ feature->sdio_clock = FEATURE_SDIO_CLOCK; }else if (aicbsp_sdiodev->chipid == PRODUCT_ID_AIC8800D80){ feature->sdio_clock = FEATURE_SDIO_CLOCK_V3; } feature->sdio_phase = FEATURE_SDIO_PHASE; feature->hwinfo = aicbsp_info.hwinfo; feature->fwlog_en = aicbsp_info.fwlog_en; if(fw_path != NULL){ sprintf(fw_path,"%s", AICBSP_FW_PATH); } sdio_dbg("%s, set FEATURE_SDIO_CLOCK %d MHz\n", __func__, feature->sdio_clock/1000000); return 0; } EXPORT_SYMBOL_GPL(aicbsp_get_feature);