diff --git a/cnss2/bus.c b/cnss2/bus.c index de129c5e5f..dc8a39db0d 100644 --- a/cnss2/bus.c +++ b/cnss2/bus.c @@ -139,6 +139,21 @@ void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv, } } +int cnss_bus_load_tme_patch(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_load_tme_patch(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) { if (!plat_priv) diff --git a/cnss2/bus.h b/cnss2/bus.h index 61f059235c..ea7e98e96c 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -21,6 +21,7 @@ int cnss_bus_init(struct cnss_plat_data *plat_priv); void cnss_bus_deinit(struct cnss_plat_data *plat_priv); void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv, char *prefix_name, char *name); +int cnss_bus_load_tme_patch(struct cnss_plat_data *plat_priv); int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); int cnss_bus_load_aux(struct cnss_plat_data *plat_priv); int cnss_bus_handle_dev_sol_irq(struct cnss_plat_data *plat_priv); diff --git a/cnss2/main.c b/cnss2/main.c index d881bf20a4..0f6f49f7bf 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -842,6 +842,11 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; + cnss_bus_load_tme_patch(plat_priv); + + cnss_wlfw_tme_patch_dnld_send_sync(plat_priv, + WLFW_TME_LITE_PATCH_FILE_V01); + if (plat_priv->hds_enabled) cnss_wlfw_bdf_dnld_send_sync(plat_priv, CNSS_BDF_HDS); diff --git a/cnss2/main.h b/cnss2/main.h index c3eb650568..737f1a0a31 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -548,6 +548,7 @@ struct cnss_plat_data { u32 fw_mem_seg_len; struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; struct cnss_fw_mem m3_mem; + struct cnss_fw_mem tme_lite_mem; struct cnss_fw_mem *cal_mem; struct cnss_fw_mem aux_mem; u64 cal_time; diff --git a/cnss2/pci.c b/cnss2/pci.c index e32a138e68..8e63578921 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -49,6 +49,7 @@ #define DEFAULT_PHY_M3_FILE_NAME "m3.bin" #define DEFAULT_AUX_FILE_NAME "aux_ucode.elf" #define DEFAULT_PHY_UCODE_FILE_NAME "phy_ucode.elf" +#define TME_PATCH_FILE_NAME "tmel_patch.elf" #define PHY_UCODE_V2_FILE_NAME "phy_ucode20.elf" #define DEFAULT_FW_FILE_NAME "amss.bin" #define FW_V2_FILE_NAME "amss20.bin" @@ -4762,6 +4763,78 @@ void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv) plat_priv->qdss_mem_seg_len = 0; } +int cnss_pci_load_tme_patch(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *tme_lite_mem = &plat_priv->tme_lite_mem; + char filename[MAX_FIRMWARE_NAME_LEN]; + char *tme_patch_filename = NULL; + const struct firmware *fw_entry; + int ret = 0; + + switch (pci_priv->device_id) { + case PEACH_DEVICE_ID: + tme_patch_filename = TME_PATCH_FILE_NAME; + break; + case QCA6174_DEVICE_ID: + case QCA6290_DEVICE_ID: + case QCA6390_DEVICE_ID: + case QCA6490_DEVICE_ID: + case KIWI_DEVICE_ID: + case MANGO_DEVICE_ID: + default: + cnss_pr_dbg("TME-L not supported for device ID: (0x%x)\n", + pci_priv->device_id); + return 0; + } + + if (!tme_lite_mem->va && !tme_lite_mem->size) { + cnss_pci_add_fw_prefix_name(pci_priv, filename, + tme_patch_filename); + + ret = firmware_request_nowarn(&fw_entry, filename, + &pci_priv->pci_dev->dev); + if (ret) { + cnss_pr_err("Failed to load TME-L patch: %s, ret: %d\n", + filename, ret); + return ret; + } + + tme_lite_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev, + fw_entry->size, &tme_lite_mem->pa, + GFP_KERNEL); + if (!tme_lite_mem->va) { + cnss_pr_err("Failed to allocate memory for M3, size: 0x%zx\n", + fw_entry->size); + release_firmware(fw_entry); + return -ENOMEM; + } + + memcpy(tme_lite_mem->va, fw_entry->data, fw_entry->size); + tme_lite_mem->size = fw_entry->size; + release_firmware(fw_entry); + } + + return 0; +} + +static void cnss_pci_free_tme_lite_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *tme_lite_mem = &plat_priv->tme_lite_mem; + + if (tme_lite_mem->va && tme_lite_mem->size) { + cnss_pr_dbg("Freeing memory for TME patch, va: 0x%pK, pa: %pa, size: 0x%zx\n", + tme_lite_mem->va, &tme_lite_mem->pa, tme_lite_mem->size); + dma_free_coherent(&pci_priv->pci_dev->dev, tme_lite_mem->size, + tme_lite_mem->va, tme_lite_mem->pa); + } + + tme_lite_mem->va = NULL; + tme_lite_mem->pa = 0; + tme_lite_mem->size = 0; +} + int cnss_pci_load_m3(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -7123,6 +7196,7 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) clear_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state); cnss_pci_unregister_driver_hdlr(pci_priv); cnss_pci_free_aux_mem(pci_priv); + cnss_pci_free_tme_lite_mem(pci_priv); cnss_pci_free_m3_mem(pci_priv); cnss_pci_free_fw_mem(pci_priv); cnss_pci_free_qdss_mem(pci_priv); diff --git a/cnss2/pci.h b/cnss2/pci.h index 9c941b9693..c5160f85e6 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -253,6 +253,7 @@ void cnss_pci_add_fw_prefix_name(struct cnss_pci_data *pci_priv, int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv); int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv); void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv); +int cnss_pci_load_tme_patch(struct cnss_pci_data *pci_priv); int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); void cnss_pci_free_blob_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_aux(struct cnss_pci_data *pci_priv); diff --git a/cnss2/qmi.c b/cnss2/qmi.c index 8012406668..2e0c33f94c 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -1017,6 +1017,87 @@ err_req_fw: return ret; } +int cnss_wlfw_tme_patch_dnld_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file) +{ + struct wlfw_tme_lite_info_req_msg_v01 *req; + struct wlfw_tme_lite_info_resp_msg_v01 *resp; + struct qmi_txn txn; + struct cnss_fw_mem *tme_lite_mem = &plat_priv->tme_lite_mem; + int ret = 0; + + cnss_pr_dbg("Sending TME patch information message, state: 0x%lx\n", + plat_priv->driver_state); + + if (plat_priv->device_id != PEACH_DEVICE_ID) + return 0; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + + if (!tme_lite_mem->pa || !tme_lite_mem->size) { + cnss_pr_err("Memory for TME patch is not available\n"); + ret = -ENOMEM; + goto out; + } + + cnss_pr_dbg("TME-L patch memory, va: 0x%pK, pa: %pa, size: 0x%zx\n", + tme_lite_mem->va, &tme_lite_mem->pa, tme_lite_mem->size); + + req->tme_file = file; + req->addr = plat_priv->tme_lite_mem.pa; + req->size = plat_priv->tme_lite_mem.size; + + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, + wlfw_tme_lite_info_resp_msg_v01_ei, resp); + if (ret < 0) { + cnss_pr_err("Failed to initialize txn for TME patch information request, err: %d\n", + ret); + goto out; + } + + ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn, + QMI_WLFW_TME_LITE_INFO_REQ_V01, + WLFW_TME_LITE_INFO_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_tme_lite_info_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + cnss_pr_err("Failed to send TME patch information request, err: %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF); + if (ret < 0) { + cnss_pr_err("Failed to wait for response of TME patch information request, err: %d\n", + ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("TME patch information request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = -resp->resp.result; + goto out; + } + + kfree(req); + kfree(resp); + return 0; + +out: + kfree(req); + kfree(resp); + return ret; +} + int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv) { struct wlfw_m3_info_req_msg_v01 *req; diff --git a/cnss2/qmi.h b/cnss2/qmi.h index e85b3554e8..904408b70a 100644 --- a/cnss2/qmi.h +++ b/cnss2/qmi.h @@ -44,6 +44,8 @@ int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, u32 bdf_type); +int cnss_wlfw_tme_patch_dnld_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file); int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_aux_dnld_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv,