},
};
+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,
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)
{
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);
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);
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;
}
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;
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);
}
.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,
.board_size = 256 * 1024,
.cal_offset = 256 * 1024,
.m3_loader = ath12k_m3_fw_loader_driver,
+ .download_aux_ucode = false,
},
.max_radios = 1,
.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,
.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,
.board_size = 256 * 1024,
.cal_offset = 256 * 1024,
.m3_loader = ath12k_m3_fw_loader_driver,
+ .download_aux_ucode = true,
},
.max_radios = 1,
.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,