diff --git a/cnss2/bus.c b/cnss2/bus.c index e7558189e8..de129c5e5f 100644 --- a/cnss2/bus.c +++ b/cnss2/bus.c @@ -154,6 +154,21 @@ int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) } } +int cnss_bus_load_aux(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_load_aux(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + int cnss_bus_handle_dev_sol_irq(struct cnss_plat_data *plat_priv) { if (!plat_priv) diff --git a/cnss2/bus.h b/cnss2/bus.h index 679c85ec61..61f059235c 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -22,6 +22,7 @@ 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_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); int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv); int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv); diff --git a/cnss2/main.c b/cnss2/main.c index 5a104368ce..e27b1c736a 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -811,6 +811,23 @@ int cnss_set_pcie_gen_speed(struct device *dev, u8 pcie_gen_speed) } EXPORT_SYMBOL(cnss_set_pcie_gen_speed); +static bool cnss_is_aux_support_enabled(struct cnss_plat_data *plat_priv) +{ + switch (plat_priv->device_id) { + case PEACH_DEVICE_ID: + if (!plat_priv->fw_aux_uc_support) { + cnss_pr_dbg("FW does not support aux uc capability\n"); + return false; + } + break; + default: + cnss_pr_dbg("Host does not support aux uc capability\n"); + return false; + } + + return true; +} + static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -850,6 +867,16 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; + if (cnss_is_aux_support_enabled(plat_priv)) { + ret = cnss_bus_load_aux(plat_priv); + if (ret) + goto out; + + ret = cnss_wlfw_aux_dnld_send_sync(plat_priv); + if (ret) + goto out; + } + cnss_wlfw_qdss_dnld_send_sync(plat_priv); return 0; @@ -4377,6 +4404,8 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) if (of_property_read_bool(plat_priv->plat_dev->dev.of_node, "qcom,rc-ep-short-channel")) cnss_set_feature_list(plat_priv, CNSS_RC_EP_ULTRASHORT_CHANNEL_V01); + if (plat_priv->device_id == PEACH_DEVICE_ID) + cnss_set_feature_list(plat_priv, CNSS_AUX_UC_SUPPORT_V01); return 0; } diff --git a/cnss2/main.h b/cnss2/main.h index a2d1ca6e5a..a73f1aad63 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -269,6 +269,7 @@ enum cnss_mem_type { CNSS_MEM_M3, CNSS_MEM_CAL_V01, CNSS_MEM_DPD_V01, + CNSS_MEM_AUX, }; enum cnss_fw_dump_type { @@ -535,6 +536,7 @@ struct cnss_plat_data { struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; struct cnss_fw_mem m3_mem; struct cnss_fw_mem *cal_mem; + struct cnss_fw_mem aux_mem; u64 cal_time; bool cbc_file_download; u32 cal_file_size; @@ -583,6 +585,7 @@ struct cnss_plat_data { u8 set_wlaon_pwr_ctrl; struct cnss_tcs_info tcs_info; bool fw_pcie_gen_switch; + bool fw_aux_uc_support; u64 fw_caps; u8 pcie_gen_speed; struct iommu_domain *audio_iommu_domain; diff --git a/cnss2/pci.c b/cnss2/pci.c index 86e89ea983..e6447a8240 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -47,6 +47,7 @@ #define MANGO_PATH_PREFIX "mango/" #define PEACH_PATH_PREFIX "peach/" #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 PHY_UCODE_V2_FILE_NAME "phy_ucode20.elf" #define DEFAULT_FW_FILE_NAME "amss.bin" @@ -4855,6 +4856,61 @@ void cnss_pci_free_blob_mem(struct cnss_pci_data *pci_priv) } #endif +int cnss_pci_load_aux(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *aux_mem = &plat_priv->aux_mem; + char filename[MAX_FIRMWARE_NAME_LEN]; + char *aux_filename = DEFAULT_AUX_FILE_NAME; + const struct firmware *fw_entry; + int ret = 0; + + if (!aux_mem->va && !aux_mem->size) { + cnss_pci_add_fw_prefix_name(pci_priv, filename, + aux_filename); + + ret = firmware_request_nowarn(&fw_entry, filename, + &pci_priv->pci_dev->dev); + if (ret) { + cnss_pr_err("Failed to load AUX image: %s\n", filename); + return ret; + } + + aux_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev, + fw_entry->size, &aux_mem->pa, + GFP_KERNEL); + if (!aux_mem->va) { + cnss_pr_err("Failed to allocate memory for AUX, size: 0x%zx\n", + fw_entry->size); + release_firmware(fw_entry); + return -ENOMEM; + } + + memcpy(aux_mem->va, fw_entry->data, fw_entry->size); + aux_mem->size = fw_entry->size; + release_firmware(fw_entry); + } + + return 0; +} + +static void cnss_pci_free_aux_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *aux_mem = &plat_priv->aux_mem; + + if (aux_mem->va && aux_mem->size) { + cnss_pr_dbg("Freeing memory for AUX, va: 0x%pK, pa: %pa, size: 0x%zx\n", + aux_mem->va, &aux_mem->pa, aux_mem->size); + dma_free_coherent(&pci_priv->pci_dev->dev, aux_mem->size, + aux_mem->va, aux_mem->pa); + } + + aux_mem->va = NULL; + aux_mem->pa = 0; + aux_mem->size = 0; +} + void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv; @@ -7001,6 +7057,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_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 c2d86beda4..9c941b9693 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -255,6 +255,7 @@ 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_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); int cnss_pci_handle_dev_sol_irq(struct cnss_pci_data *pci_priv); int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic); diff --git a/cnss2/qmi.c b/cnss2/qmi.c index f17d3d62b8..8012406668 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -590,6 +590,10 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) if (resp->fw_caps_valid) { plat_priv->fw_pcie_gen_switch = !!(resp->fw_caps & QMI_WLFW_HOST_PCIE_GEN_SWITCH_V01); + plat_priv->fw_aux_uc_support = + !!(resp->fw_caps & QMI_WLFW_AUX_UC_SUPPORT_V01); + cnss_pr_dbg("FW aux uc support capability: %d\n", + plat_priv->fw_aux_uc_support); plat_priv->fw_caps = resp->fw_caps; } @@ -1090,6 +1094,83 @@ out: return ret; } +int cnss_wlfw_aux_dnld_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_aux_uc_info_req_msg_v01 *req; + struct wlfw_aux_uc_info_resp_msg_v01 *resp; + struct qmi_txn txn; + struct cnss_fw_mem *aux_mem = &plat_priv->aux_mem; + int ret = 0; + + cnss_pr_dbg("Sending QMI_WLFW_AUX_UC_INFO_REQ_V01 message, state: 0x%lx\n", + plat_priv->driver_state); + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + + if (!aux_mem->pa || !aux_mem->size) { + cnss_pr_err("Memory for AUX is not available\n"); + ret = -ENOMEM; + goto out; + } + + cnss_pr_dbg("AUX memory, va: 0x%pK, pa: %pa, size: 0x%zx\n", + aux_mem->va, &aux_mem->pa, aux_mem->size); + + req->addr = plat_priv->aux_mem.pa; + req->size = plat_priv->aux_mem.size; + + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, + wlfw_aux_uc_info_resp_msg_v01_ei, resp); + if (ret < 0) { + cnss_pr_err("Failed to initialize txn for QMI_WLFW_AUX_UC_INFO_REQ_V01 request, err: %d\n", + ret); + goto out; + } + + ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn, + QMI_WLFW_AUX_UC_INFO_REQ_V01, + WLFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_aux_uc_info_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + cnss_pr_err("Failed to send QMI_WLFW_AUX_UC_INFO_REQ_V01 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 QMI_WLFW_AUX_UC_INFO_REQ_V01 request, err: %d\n", + ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("QMI_WLFW_AUX_UC_INFO_REQ_V01 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: + CNSS_QMI_ASSERT(); + kfree(req); + kfree(resp); + return ret; +} + int cnss_wlfw_wlan_mac_req_send_sync(struct cnss_plat_data *plat_priv, u8 *mac, u32 mac_len) { diff --git a/cnss2/qmi.h b/cnss2/qmi.h index d06405213e..e85b3554e8 100644 --- a/cnss2/qmi.h +++ b/cnss2/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ @@ -45,6 +45,7 @@ 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_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, enum cnss_driver_mode mode); int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, @@ -145,6 +146,11 @@ static inline int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv) return 0; } +static inline int cnss_wlfw_aux_dnld_send_sync(struct cnss_plat_data *plat_priv) +{ + return 0; +} + static inline int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, enum cnss_driver_mode mode) diff --git a/inc/cnss2.h b/inc/cnss2.h index 961b95f576..4636bf7bb9 100644 --- a/inc/cnss2.h +++ b/inc/cnss2.h @@ -248,6 +248,7 @@ enum cnss_recovery_reason { enum cnss_fw_caps { CNSS_FW_CAP_DIRECT_LINK_SUPPORT, + CNSS_FW_CAP_AUX_UC_SUPPORT, }; enum cnss_remote_mem_type {