]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: support downloading auxiliary ucode image for QCC2072
authorBaochen Qiang <baochen.qiang@oss.qualcomm.com>
Mon, 12 Jan 2026 07:36:30 +0000 (15:36 +0800)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Fri, 16 Jan 2026 01:19:40 +0000 (17:19 -0800)
QCC2072 requires another firmware image named aux_ucode.bin, add support
to download it.

Add a new hardware parameter download_aux_ucode to make sure other chips
are not affected.

Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3

Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20260112-ath12k-support-qcc2072-v2-10-fc8ce1e43969@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/fw.c
drivers/net/wireless/ath/ath12k/fw.h
drivers/net/wireless/ath/ath12k/hw.h
drivers/net/wireless/ath/ath12k/qmi.c
drivers/net/wireless/ath/ath12k/qmi.h
drivers/net/wireless/ath/ath12k/wifi7/hw.c

index 667cf5993cf155a08bc26899453db1045b2e934d..990934ec92fcad1fede77e88aa5a0efe2cf73ee0 100644 (file)
@@ -1082,6 +1082,8 @@ struct ath12k_base {
                size_t amss_dualmac_len;
                const u8 *m3_data;
                size_t m3_len;
+               const u8 *aux_uc_data;
+               size_t aux_uc_len;
 
                DECLARE_BITMAP(fw_features, ATH12K_FW_FEATURE_COUNT);
                bool fw_features_valid;
index 5ac497f80cad8718fff5979f2a9cdd9116c7d01e..22074653cbb82f31552bac5d36ddc667fff9d9e8 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
- * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #include "core.h"
@@ -121,6 +121,14 @@ static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab,
                        ab->fw.m3_data = data;
                        ab->fw.m3_len = ie_len;
                        break;
+               case ATH12K_FW_IE_AUX_UC_IMAGE:
+                       ath12k_dbg(ab, ATH12K_DBG_BOOT,
+                                  "found aux_uc image ie (%zd B)\n",
+                                  ie_len);
+
+                       ab->fw.aux_uc_data = data;
+                       ab->fw.aux_uc_len = ie_len;
+                       break;
                case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE:
                        ath12k_dbg(ab, ATH12K_DBG_BOOT,
                                   "found dualmac fw image ie (%zd B)\n",
index 7afaefed5086f19a742e0d6b5af04f210c0fd5a9..e146d24dfea4dfc2933944341512ac0b2e2cc646 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
- * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #ifndef ATH12K_FW_H
@@ -15,6 +15,7 @@ enum ath12k_fw_ie_type {
        ATH12K_FW_IE_AMSS_IMAGE = 2,
        ATH12K_FW_IE_M3_IMAGE = 3,
        ATH12K_FW_IE_AMSS_DUALMAC_IMAGE = 4,
+       ATH12K_FW_IE_AUX_UC_IMAGE = 5,
 };
 
 enum ath12k_fw_features {
index 655753d0413aecf6eddd5afa68be81202128e8dc..a9888e0521a1d1469bc980bdddeeee0a66c9b94d 100644 (file)
@@ -78,6 +78,7 @@
 #define ATH12K_DEFAULT_CAL_FILE                "caldata.bin"
 #define ATH12K_AMSS_FILE               "amss.bin"
 #define ATH12K_M3_FILE                 "m3.bin"
+#define ATH12K_AUX_UC_FILE             "aux_ucode.bin"
 #define ATH12K_REGDB_FILE_NAME         "regdb.bin"
 
 #define ATH12K_PCIE_MAX_PAYLOAD_SIZE   128
@@ -142,6 +143,7 @@ struct ath12k_hw_params {
                size_t board_size;
                size_t cal_offset;
                enum ath12k_m3_fw_loaders m3_loader;
+               bool download_aux_ucode:1;
        } fw;
 
        u8 max_radios;
index 4966697f4e6255fede8ba614f6bbaaed4471921f..cfde4147c8fc9f561cd68c28547d8b4a9e29e6f8 100644 (file)
@@ -1623,6 +1623,47 @@ static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = {
        },
 };
 
+static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_req_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_8_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(u64),
+               .array_type     = NO_ARRAY,
+               .tlv_type       = 0x01,
+               .offset         = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, addr),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(u32),
+               .array_type     = NO_ARRAY,
+               .tlv_type       = 0x02,
+               .offset         = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, size),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .array_type     = NO_ARRAY,
+               .tlv_type       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_resp_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct qmi_response_type_v01),
+               .array_type     = NO_ARRAY,
+               .tlv_type       = 0x02,
+               .offset         = offsetof(struct qmi_wlanfw_aux_uc_info_resp_msg_v01, resp),
+               .ei_array       = qmi_response_type_v01_ei,
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .array_type     = NO_ARRAY,
+               .tlv_type       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
 static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
        {
                .data_type      = QMI_UNSIGNED_4_BYTE,
@@ -3237,6 +3278,131 @@ out:
        return ret;
 }
 
+static void ath12k_qmi_aux_uc_free(struct ath12k_base *ab)
+{
+       struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem;
+
+       if (!aux_uc_mem->vaddr)
+               return;
+
+       dma_free_coherent(ab->dev, aux_uc_mem->total_size,
+                         aux_uc_mem->vaddr, aux_uc_mem->paddr);
+       aux_uc_mem->vaddr = NULL;
+       aux_uc_mem->total_size = 0;
+       aux_uc_mem->size = 0;
+}
+
+static int ath12k_qmi_aux_uc_load(struct ath12k_base *ab)
+{
+       struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem;
+       const struct firmware *fw = NULL;
+       const void *aux_uc_data;
+       char path[100];
+       size_t aux_uc_len;
+       int ret;
+
+       if (ab->fw.aux_uc_data && ab->fw.aux_uc_len > 0) {
+               /* firmware-N.bin had a aux_uc firmware file so use that */
+               aux_uc_data = ab->fw.aux_uc_data;
+               aux_uc_len = ab->fw.aux_uc_len;
+       } else {
+               /*
+                * No aux_uc file in firmware-N.bin so try to request old
+                * separate aux_ucode.bin.
+                */
+               fw = ath12k_core_firmware_request(ab, ATH12K_AUX_UC_FILE);
+               if (IS_ERR(fw)) {
+                       ret = PTR_ERR(fw);
+                       ath12k_core_create_firmware_path(ab, ATH12K_AUX_UC_FILE,
+                                                        path, sizeof(path));
+                       ath12k_err(ab, "failed to load %s: %d\n", path, ret);
+                       return ret;
+               }
+
+               aux_uc_data = fw->data;
+               aux_uc_len = fw->size;
+       }
+
+       /* In recovery/resume cases, AUX_UC buffer is not freed, try to reuse that */
+       if (aux_uc_mem->vaddr) {
+               if (aux_uc_mem->total_size >= aux_uc_len)
+                       goto copy;
+
+               /* Old buffer is too small, free and reallocate */
+               ath12k_qmi_aux_uc_free(ab);
+       }
+
+       aux_uc_mem->vaddr = dma_alloc_coherent(ab->dev, aux_uc_len,
+                                              &aux_uc_mem->paddr, GFP_KERNEL);
+       if (!aux_uc_mem->vaddr) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+        aux_uc_mem->total_size = aux_uc_len;
+
+copy:
+       memcpy(aux_uc_mem->vaddr, aux_uc_data, aux_uc_len);
+       aux_uc_mem->size = aux_uc_len;
+
+       ret = 0;
+
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static noinline_for_stack
+int ath12k_qmi_wlanfw_aux_uc_info_send(struct ath12k_base *ab)
+{
+       struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem;
+       struct qmi_wlanfw_aux_uc_info_req_msg_v01 req = {};
+       struct qmi_wlanfw_aux_uc_info_resp_msg_v01 resp = {};
+       struct qmi_txn txn;
+       int ret = 0;
+
+       ret = ath12k_qmi_aux_uc_load(ab);
+       if (ret) {
+               ath12k_err(ab, "failed to load aux_uc firmware: %d", ret);
+               return ret;
+       }
+
+       req.addr = aux_uc_mem->paddr;
+       req.size = aux_uc_mem->size;
+
+       ret = qmi_txn_init(&ab->qmi.handle, &txn,
+                          qmi_wlanfw_aux_uc_info_resp_msg_v01_ei, &resp);
+       if (ret < 0)
+               goto out;
+
+       ret = qmi_send_request(&ab->qmi.handle, NULL, &txn,
+                              QMI_WLANFW_AUX_UC_INFO_REQ_V01,
+                              QMI_WLANFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+                              qmi_wlanfw_aux_uc_info_req_msg_v01_ei, &req);
+       if (ret < 0) {
+               qmi_txn_cancel(&txn);
+               ath12k_warn(ab, "qmi failed to send AUX_UC information request, err = %d\n",
+                           ret);
+               goto out;
+       }
+
+       ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS));
+       if (ret < 0) {
+               ath12k_warn(ab, "qmi failed AUX_UC information request %d\n", ret);
+               goto out;
+       }
+
+       if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+               ath12k_warn(ab, "qmi AUX_UC info request failed, result: %d, err: %d\n",
+                           resp.resp.result, resp.resp.error);
+               ret = -EINVAL;
+               goto out;
+       }
+out:
+       return ret;
+}
+
 static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab,
                                       u32 mode)
 {
@@ -3601,6 +3767,7 @@ static noinline_for_stack
 int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi)
 {
        struct ath12k_base *ab = qmi->ab;
+       const struct ath12k_hw_params *hw_params = ab->hw_params;
        int ret;
 
        ret = ath12k_qmi_request_target_cap(ab);
@@ -3621,7 +3788,7 @@ int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi)
                return ret;
        }
 
-       if (ab->hw_params->download_calib) {
+       if (hw_params->download_calib) {
                ret = ath12k_qmi_load_bdf_qmi(ab, ATH12K_QMI_BDF_TYPE_CALIBRATION);
                if (ret < 0)
                        ath12k_warn(ab, "qmi failed to load calibrated data :%d\n", ret);
@@ -3633,6 +3800,14 @@ int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi)
                return ret;
        }
 
+       if (hw_params->fw.download_aux_ucode) {
+               ret = ath12k_qmi_wlanfw_aux_uc_info_send(ab);
+               if (ret < 0) {
+                       ath12k_warn(ab, "qmi failed to send aux_uc info req: %d\n", ret);
+                       return ret;
+               }
+       }
+
        return ret;
 }
 
@@ -3906,6 +4081,7 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab)
        qmi_handle_release(&ab->qmi.handle);
        cancel_work_sync(&ab->qmi.event_work);
        destroy_workqueue(ab->qmi.event_wq);
+       ath12k_qmi_aux_uc_free(ab);
        ath12k_qmi_m3_free(ab);
        ath12k_qmi_free_target_mem_chunk(ab);
        ab->qmi.ab = NULL;
@@ -3914,5 +4090,6 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab)
 void ath12k_qmi_free_resource(struct ath12k_base *ab)
 {
        ath12k_qmi_free_target_mem_chunk(ab);
+       ath12k_qmi_aux_uc_free(ab);
        ath12k_qmi_m3_free(ab);
 }
index 050dcaca1cb7539a010b8f70ee5794e001005839..b5a4a01391cbacc121e0c6f2f20276a75d171340 100644 (file)
@@ -154,6 +154,7 @@ struct ath12k_qmi {
        u8 num_radios;
        struct target_info target;
        struct m3_mem_region m3_mem;
+       struct m3_mem_region aux_uc_mem;
        unsigned int service_ins_id;
        struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01];
 };
@@ -203,6 +204,7 @@ enum ath12k_qmi_cnss_feature {
        CNSS_FEATURE_MIN_ENUM_VAL_V01 = INT_MIN,
        CNSS_QDSS_CFG_MISS_V01 = 3,
        CNSS_PCIE_PERST_NO_PULL_V01 = 4,
+       CNSS_AUX_UC_SUPPORT_V01 = 6,
        CNSS_MAX_FEATURE_V01 = 64,
        CNSS_FEATURE_MAX_ENUM_VAL_V01 = INT_MAX,
 };
@@ -541,6 +543,19 @@ struct qmi_wlanfw_m3_info_resp_msg_v01 {
        struct qmi_response_type_v01 resp;
 };
 
+#define QMI_WLANFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
+#define QMI_WLANFW_AUX_UC_INFO_RESP_MSG_V01_MAX_MSG_LEN        7
+#define QMI_WLANFW_AUX_UC_INFO_REQ_V01 0x005A
+
+struct qmi_wlanfw_aux_uc_info_req_msg_v01 {
+       u64 addr;
+       u32 size;
+};
+
+struct qmi_wlanfw_aux_uc_info_resp_msg_v01 {
+       struct qmi_response_type_v01 resp;
+};
+
 #define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN       11
 #define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN      7
 #define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN                803
index ef0a59f6339cfad7535eb8ffcabf536ad42b7319..38c388319e008d7c2ab40d1ca5b783cc47816c2e 100644 (file)
@@ -339,6 +339,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                        .board_size = 256 * 1024,
                        .cal_offset = 128 * 1024,
                        .m3_loader = ath12k_m3_fw_loader_driver,
+                       .download_aux_ucode = false,
                },
                .max_radios = 1,
                .single_pdev_only = false,
@@ -421,6 +422,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                        .board_size = 256 * 1024,
                        .cal_offset = 256 * 1024,
                        .m3_loader = ath12k_m3_fw_loader_driver,
+                       .download_aux_ucode = false,
                },
 
                .max_radios = 1,
@@ -505,6 +507,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                        .board_size = 256 * 1024,
                        .cal_offset = 128 * 1024,
                        .m3_loader = ath12k_m3_fw_loader_driver,
+                       .download_aux_ucode = false,
                },
                .max_radios = 2,
                .single_pdev_only = false,
@@ -586,6 +589,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                        .board_size = 256 * 1024,
                        .cal_offset = 128 * 1024,
                        .m3_loader = ath12k_m3_fw_loader_remoteproc,
+                       .download_aux_ucode = false,
                },
                .max_radios = 1,
                .single_pdev_only = false,
@@ -661,6 +665,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                        .board_size = 256 * 1024,
                        .cal_offset = 256 * 1024,
                        .m3_loader = ath12k_m3_fw_loader_driver,
+                       .download_aux_ucode = true,
                },
 
                .max_radios = 1,
@@ -707,7 +712,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
                .wmi_init = ath12k_wifi7_wmi_init_wcn7850,
 
                .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01) |
-                                          BIT(CNSS_PCIE_PERST_NO_PULL_V01),
+                                          BIT(CNSS_PCIE_PERST_NO_PULL_V01) |
+                                          BIT(CNSS_AUX_UC_SUPPORT_V01),
 
                .rfkill_pin = 0,
                .rfkill_cfg = 0,