162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2018 The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/completion.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/idr.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/net.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/firmware/qcom/qcom_scm.h> 1762306a36Sopenharmony_ci#include <linux/soc/qcom/smem.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <net/sock.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "debug.h" 2262306a36Sopenharmony_ci#include "snoc.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define ATH10K_QMI_CLIENT_ID 0x4b4e454c 2562306a36Sopenharmony_ci#define ATH10K_QMI_TIMEOUT 30 2662306a36Sopenharmony_ci#define SMEM_IMAGE_VERSION_TABLE 469 2762306a36Sopenharmony_ci#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 2862306a36Sopenharmony_ci#define SMEM_IMAGE_VERSION_ENTRY_SIZE 128 2962306a36Sopenharmony_ci#define SMEM_IMAGE_VERSION_NAME_SIZE 75 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi, 3262306a36Sopenharmony_ci struct ath10k_msa_mem_info *mem_info) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct qcom_scm_vmperm dst_perms[3]; 3562306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 3662306a36Sopenharmony_ci u64 src_perms; 3762306a36Sopenharmony_ci u32 perm_count; 3862306a36Sopenharmony_ci int ret; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci src_perms = BIT(QCOM_SCM_VMID_HLOS); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci dst_perms[0].vmid = QCOM_SCM_VMID_MSS_MSA; 4362306a36Sopenharmony_ci dst_perms[0].perm = QCOM_SCM_PERM_RW; 4462306a36Sopenharmony_ci dst_perms[1].vmid = QCOM_SCM_VMID_WLAN; 4562306a36Sopenharmony_ci dst_perms[1].perm = QCOM_SCM_PERM_RW; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (mem_info->secure) { 4862306a36Sopenharmony_ci perm_count = 2; 4962306a36Sopenharmony_ci } else { 5062306a36Sopenharmony_ci dst_perms[2].vmid = QCOM_SCM_VMID_WLAN_CE; 5162306a36Sopenharmony_ci dst_perms[2].perm = QCOM_SCM_PERM_RW; 5262306a36Sopenharmony_ci perm_count = 3; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size, 5662306a36Sopenharmony_ci &src_perms, dst_perms, perm_count); 5762306a36Sopenharmony_ci if (ret < 0) 5862306a36Sopenharmony_ci ath10k_err(ar, "failed to assign msa map permissions: %d\n", ret); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi, 6462306a36Sopenharmony_ci struct ath10k_msa_mem_info *mem_info) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct qcom_scm_vmperm dst_perms; 6762306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 6862306a36Sopenharmony_ci u64 src_perms; 6962306a36Sopenharmony_ci int ret; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!mem_info->secure) 7462306a36Sopenharmony_ci src_perms |= BIT(QCOM_SCM_VMID_WLAN_CE); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci dst_perms.vmid = QCOM_SCM_VMID_HLOS; 7762306a36Sopenharmony_ci dst_perms.perm = QCOM_SCM_PERM_RW; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size, 8062306a36Sopenharmony_ci &src_perms, &dst_perms, 1); 8162306a36Sopenharmony_ci if (ret < 0) 8262306a36Sopenharmony_ci ath10k_err(ar, "failed to unmap msa permissions: %d\n", ret); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int ret; 9062306a36Sopenharmony_ci int i; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (qmi->msa_fixed_perm) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (i = 0; i < qmi->nr_mem_region; i++) { 9662306a36Sopenharmony_ci ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci goto err_unmap; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cierr_unmap: 10462306a36Sopenharmony_ci for (i--; i >= 0; i--) 10562306a36Sopenharmony_ci ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]); 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (qmi->msa_fixed_perm) 11462306a36Sopenharmony_ci return; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (i = 0; i < qmi->nr_mem_region; i++) 11762306a36Sopenharmony_ci ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi *qmi) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct wlfw_msa_info_resp_msg_v01 resp = {}; 12362306a36Sopenharmony_ci struct wlfw_msa_info_req_msg_v01 req = {}; 12462306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 12562306a36Sopenharmony_ci phys_addr_t max_mapped_addr; 12662306a36Sopenharmony_ci struct qmi_txn txn; 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci int i; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci req.msa_addr = ar->msa.paddr; 13162306a36Sopenharmony_ci req.size = ar->msa.mem_size; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 13462306a36Sopenharmony_ci wlfw_msa_info_resp_msg_v01_ei, &resp); 13562306a36Sopenharmony_ci if (ret < 0) 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 13962306a36Sopenharmony_ci QMI_WLFW_MSA_INFO_REQ_V01, 14062306a36Sopenharmony_ci WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN, 14162306a36Sopenharmony_ci wlfw_msa_info_req_msg_v01_ei, &req); 14262306a36Sopenharmony_ci if (ret < 0) { 14362306a36Sopenharmony_ci qmi_txn_cancel(&txn); 14462306a36Sopenharmony_ci ath10k_err(ar, "failed to send msa mem info req: %d\n", ret); 14562306a36Sopenharmony_ci goto out; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 14962306a36Sopenharmony_ci if (ret < 0) 15062306a36Sopenharmony_ci goto out; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 15362306a36Sopenharmony_ci ath10k_err(ar, "msa info req rejected: %d\n", resp.resp.error); 15462306a36Sopenharmony_ci ret = -EINVAL; 15562306a36Sopenharmony_ci goto out; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (resp.mem_region_info_len > QMI_WLFW_MAX_MEM_REG_V01) { 15962306a36Sopenharmony_ci ath10k_err(ar, "invalid memory region length received: %d\n", 16062306a36Sopenharmony_ci resp.mem_region_info_len); 16162306a36Sopenharmony_ci ret = -EINVAL; 16262306a36Sopenharmony_ci goto out; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci max_mapped_addr = ar->msa.paddr + ar->msa.mem_size; 16662306a36Sopenharmony_ci qmi->nr_mem_region = resp.mem_region_info_len; 16762306a36Sopenharmony_ci for (i = 0; i < resp.mem_region_info_len; i++) { 16862306a36Sopenharmony_ci if (resp.mem_region_info[i].size > ar->msa.mem_size || 16962306a36Sopenharmony_ci resp.mem_region_info[i].region_addr > max_mapped_addr || 17062306a36Sopenharmony_ci resp.mem_region_info[i].region_addr < ar->msa.paddr || 17162306a36Sopenharmony_ci resp.mem_region_info[i].size + 17262306a36Sopenharmony_ci resp.mem_region_info[i].region_addr > max_mapped_addr) { 17362306a36Sopenharmony_ci ath10k_err(ar, "received out of range memory region address 0x%llx with size 0x%x, aborting\n", 17462306a36Sopenharmony_ci resp.mem_region_info[i].region_addr, 17562306a36Sopenharmony_ci resp.mem_region_info[i].size); 17662306a36Sopenharmony_ci ret = -EINVAL; 17762306a36Sopenharmony_ci goto fail_unwind; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci qmi->mem_region[i].addr = resp.mem_region_info[i].region_addr; 18062306a36Sopenharmony_ci qmi->mem_region[i].size = resp.mem_region_info[i].size; 18162306a36Sopenharmony_ci qmi->mem_region[i].secure = resp.mem_region_info[i].secure_flag; 18262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, 18362306a36Sopenharmony_ci "qmi msa mem region %d addr 0x%pa size 0x%x flag 0x%08x\n", 18462306a36Sopenharmony_ci i, &qmi->mem_region[i].addr, 18562306a36Sopenharmony_ci qmi->mem_region[i].size, 18662306a36Sopenharmony_ci qmi->mem_region[i].secure); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem info request completed\n"); 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cifail_unwind: 19362306a36Sopenharmony_ci memset(&qmi->mem_region[0], 0, sizeof(qmi->mem_region[0]) * i); 19462306a36Sopenharmony_ciout: 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int ath10k_qmi_msa_ready_send_sync_msg(struct ath10k_qmi *qmi) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct wlfw_msa_ready_resp_msg_v01 resp = {}; 20162306a36Sopenharmony_ci struct wlfw_msa_ready_req_msg_v01 req = {}; 20262306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 20362306a36Sopenharmony_ci struct qmi_txn txn; 20462306a36Sopenharmony_ci int ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 20762306a36Sopenharmony_ci wlfw_msa_ready_resp_msg_v01_ei, &resp); 20862306a36Sopenharmony_ci if (ret < 0) 20962306a36Sopenharmony_ci goto out; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 21262306a36Sopenharmony_ci QMI_WLFW_MSA_READY_REQ_V01, 21362306a36Sopenharmony_ci WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN, 21462306a36Sopenharmony_ci wlfw_msa_ready_req_msg_v01_ei, &req); 21562306a36Sopenharmony_ci if (ret < 0) { 21662306a36Sopenharmony_ci qmi_txn_cancel(&txn); 21762306a36Sopenharmony_ci ath10k_err(ar, "failed to send msa mem ready request: %d\n", ret); 21862306a36Sopenharmony_ci goto out; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 22262306a36Sopenharmony_ci if (ret < 0) 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 22662306a36Sopenharmony_ci ath10k_err(ar, "msa ready request rejected: %d\n", resp.resp.error); 22762306a36Sopenharmony_ci ret = -EINVAL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem ready request completed\n"); 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciout: 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct wlfw_bdf_download_resp_msg_v01 resp = {}; 24062306a36Sopenharmony_ci struct wlfw_bdf_download_req_msg_v01 *req; 24162306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 24262306a36Sopenharmony_ci unsigned int remaining; 24362306a36Sopenharmony_ci struct qmi_txn txn; 24462306a36Sopenharmony_ci const u8 *temp; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 24862306a36Sopenharmony_ci if (!req) 24962306a36Sopenharmony_ci return -ENOMEM; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci temp = ar->normal_mode_fw.board_data; 25262306a36Sopenharmony_ci remaining = ar->normal_mode_fw.board_len; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci while (remaining) { 25562306a36Sopenharmony_ci req->valid = 1; 25662306a36Sopenharmony_ci req->file_id_valid = 1; 25762306a36Sopenharmony_ci req->file_id = 0; 25862306a36Sopenharmony_ci req->total_size_valid = 1; 25962306a36Sopenharmony_ci req->total_size = ar->normal_mode_fw.board_len; 26062306a36Sopenharmony_ci req->seg_id_valid = 1; 26162306a36Sopenharmony_ci req->data_valid = 1; 26262306a36Sopenharmony_ci req->end_valid = 1; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { 26562306a36Sopenharmony_ci req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci req->data_len = remaining; 26862306a36Sopenharmony_ci req->end = 1; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci memcpy(req->data, temp, req->data_len); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 27462306a36Sopenharmony_ci wlfw_bdf_download_resp_msg_v01_ei, 27562306a36Sopenharmony_ci &resp); 27662306a36Sopenharmony_ci if (ret < 0) 27762306a36Sopenharmony_ci goto out; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 28062306a36Sopenharmony_ci QMI_WLFW_BDF_DOWNLOAD_REQ_V01, 28162306a36Sopenharmony_ci WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN, 28262306a36Sopenharmony_ci wlfw_bdf_download_req_msg_v01_ei, req); 28362306a36Sopenharmony_ci if (ret < 0) { 28462306a36Sopenharmony_ci qmi_txn_cancel(&txn); 28562306a36Sopenharmony_ci goto out; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (ret < 0) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* end = 1 triggers a CRC check on the BDF. If this fails, we 29462306a36Sopenharmony_ci * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still 29562306a36Sopenharmony_ci * willing to use the BDF. For some platforms, all the valid 29662306a36Sopenharmony_ci * released BDFs fail this CRC check, so attempt to detect this 29762306a36Sopenharmony_ci * scenario and treat it as non-fatal. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && 30062306a36Sopenharmony_ci !(req->end == 1 && 30162306a36Sopenharmony_ci resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) { 30262306a36Sopenharmony_ci ath10k_err(ar, "failed to download board data file: %d\n", 30362306a36Sopenharmony_ci resp.resp.error); 30462306a36Sopenharmony_ci ret = -EINVAL; 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci remaining -= req->data_len; 30962306a36Sopenharmony_ci temp += req->data_len; 31062306a36Sopenharmony_ci req->seg_id++; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi bdf download request completed\n"); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci kfree(req); 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ciout: 31962306a36Sopenharmony_ci kfree(req); 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int ath10k_qmi_send_cal_report_req(struct ath10k_qmi *qmi) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct wlfw_cal_report_resp_msg_v01 resp = {}; 32662306a36Sopenharmony_ci struct wlfw_cal_report_req_msg_v01 req = {}; 32762306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 32862306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 32962306a36Sopenharmony_ci struct qmi_txn txn; 33062306a36Sopenharmony_ci int i, j = 0; 33162306a36Sopenharmony_ci int ret; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ar_snoc->xo_cal_supported) { 33462306a36Sopenharmony_ci req.xo_cal_data_valid = 1; 33562306a36Sopenharmony_ci req.xo_cal_data = ar_snoc->xo_cal_data; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cal_report_resp_msg_v01_ei, 33962306a36Sopenharmony_ci &resp); 34062306a36Sopenharmony_ci if (ret < 0) 34162306a36Sopenharmony_ci goto out; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for (i = 0; i < QMI_WLFW_MAX_NUM_CAL_V01; i++) { 34462306a36Sopenharmony_ci if (qmi->cal_data[i].total_size && 34562306a36Sopenharmony_ci qmi->cal_data[i].data) { 34662306a36Sopenharmony_ci req.meta_data[j] = qmi->cal_data[i].cal_id; 34762306a36Sopenharmony_ci j++; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci req.meta_data_len = j; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 35362306a36Sopenharmony_ci QMI_WLFW_CAL_REPORT_REQ_V01, 35462306a36Sopenharmony_ci WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN, 35562306a36Sopenharmony_ci wlfw_cal_report_req_msg_v01_ei, &req); 35662306a36Sopenharmony_ci if (ret < 0) { 35762306a36Sopenharmony_ci qmi_txn_cancel(&txn); 35862306a36Sopenharmony_ci ath10k_err(ar, "failed to send calibration request: %d\n", ret); 35962306a36Sopenharmony_ci goto out; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 36362306a36Sopenharmony_ci if (ret < 0) 36462306a36Sopenharmony_ci goto out; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 36762306a36Sopenharmony_ci ath10k_err(ar, "calibration request rejected: %d\n", resp.resp.error); 36862306a36Sopenharmony_ci ret = -EINVAL; 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi cal report request completed\n"); 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciout: 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int 38062306a36Sopenharmony_ciath10k_qmi_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 38362306a36Sopenharmony_ci struct ath10k_qmi *qmi = ar_snoc->qmi; 38462306a36Sopenharmony_ci struct wlfw_wlan_mode_resp_msg_v01 resp = {}; 38562306a36Sopenharmony_ci struct wlfw_wlan_mode_req_msg_v01 req = {}; 38662306a36Sopenharmony_ci struct qmi_txn txn; 38762306a36Sopenharmony_ci int ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 39062306a36Sopenharmony_ci wlfw_wlan_mode_resp_msg_v01_ei, 39162306a36Sopenharmony_ci &resp); 39262306a36Sopenharmony_ci if (ret < 0) 39362306a36Sopenharmony_ci goto out; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci req.mode = mode; 39662306a36Sopenharmony_ci req.hw_debug_valid = 1; 39762306a36Sopenharmony_ci req.hw_debug = 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 40062306a36Sopenharmony_ci QMI_WLFW_WLAN_MODE_REQ_V01, 40162306a36Sopenharmony_ci WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN, 40262306a36Sopenharmony_ci wlfw_wlan_mode_req_msg_v01_ei, &req); 40362306a36Sopenharmony_ci if (ret < 0) { 40462306a36Sopenharmony_ci qmi_txn_cancel(&txn); 40562306a36Sopenharmony_ci ath10k_err(ar, "failed to send wlan mode %d request: %d\n", mode, ret); 40662306a36Sopenharmony_ci goto out; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 41062306a36Sopenharmony_ci if (ret < 0) 41162306a36Sopenharmony_ci goto out; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 41462306a36Sopenharmony_ci ath10k_err(ar, "more request rejected: %d\n", resp.resp.error); 41562306a36Sopenharmony_ci ret = -EINVAL; 41662306a36Sopenharmony_ci goto out; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wlan mode req completed: %d\n", mode); 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciout: 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int 42762306a36Sopenharmony_ciath10k_qmi_cfg_send_sync_msg(struct ath10k *ar, 42862306a36Sopenharmony_ci struct ath10k_qmi_wlan_enable_cfg *config, 42962306a36Sopenharmony_ci const char *version) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 43262306a36Sopenharmony_ci struct ath10k_qmi *qmi = ar_snoc->qmi; 43362306a36Sopenharmony_ci struct wlfw_wlan_cfg_resp_msg_v01 resp = {}; 43462306a36Sopenharmony_ci struct wlfw_wlan_cfg_req_msg_v01 *req; 43562306a36Sopenharmony_ci struct qmi_txn txn; 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci u32 i; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 44062306a36Sopenharmony_ci if (!req) 44162306a36Sopenharmony_ci return -ENOMEM; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 44462306a36Sopenharmony_ci wlfw_wlan_cfg_resp_msg_v01_ei, 44562306a36Sopenharmony_ci &resp); 44662306a36Sopenharmony_ci if (ret < 0) 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci req->host_version_valid = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci req->tgt_cfg_valid = 1; 45262306a36Sopenharmony_ci if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) 45362306a36Sopenharmony_ci req->tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci req->tgt_cfg_len = config->num_ce_tgt_cfg; 45662306a36Sopenharmony_ci for (i = 0; i < req->tgt_cfg_len; i++) { 45762306a36Sopenharmony_ci req->tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; 45862306a36Sopenharmony_ci req->tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; 45962306a36Sopenharmony_ci req->tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; 46062306a36Sopenharmony_ci req->tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; 46162306a36Sopenharmony_ci req->tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci req->svc_cfg_valid = 1; 46562306a36Sopenharmony_ci if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) 46662306a36Sopenharmony_ci req->svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci req->svc_cfg_len = config->num_ce_svc_pipe_cfg; 46962306a36Sopenharmony_ci for (i = 0; i < req->svc_cfg_len; i++) { 47062306a36Sopenharmony_ci req->svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; 47162306a36Sopenharmony_ci req->svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; 47262306a36Sopenharmony_ci req->svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci req->shadow_reg_valid = 1; 47662306a36Sopenharmony_ci if (config->num_shadow_reg_cfg > 47762306a36Sopenharmony_ci QMI_WLFW_MAX_NUM_SHADOW_REG_V01) 47862306a36Sopenharmony_ci req->shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci req->shadow_reg_len = config->num_shadow_reg_cfg; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci memcpy(req->shadow_reg, config->shadow_reg_cfg, 48362306a36Sopenharmony_ci sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req->shadow_reg_len); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 48662306a36Sopenharmony_ci QMI_WLFW_WLAN_CFG_REQ_V01, 48762306a36Sopenharmony_ci WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN, 48862306a36Sopenharmony_ci wlfw_wlan_cfg_req_msg_v01_ei, req); 48962306a36Sopenharmony_ci if (ret < 0) { 49062306a36Sopenharmony_ci qmi_txn_cancel(&txn); 49162306a36Sopenharmony_ci ath10k_err(ar, "failed to send config request: %d\n", ret); 49262306a36Sopenharmony_ci goto out; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 49662306a36Sopenharmony_ci if (ret < 0) 49762306a36Sopenharmony_ci goto out; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 50062306a36Sopenharmony_ci ath10k_err(ar, "config request rejected: %d\n", resp.resp.error); 50162306a36Sopenharmony_ci ret = -EINVAL; 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi config request completed\n"); 50662306a36Sopenharmony_ci kfree(req); 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciout: 51062306a36Sopenharmony_ci kfree(req); 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ciint ath10k_qmi_wlan_enable(struct ath10k *ar, 51562306a36Sopenharmony_ci struct ath10k_qmi_wlan_enable_cfg *config, 51662306a36Sopenharmony_ci enum wlfw_driver_mode_enum_v01 mode, 51762306a36Sopenharmony_ci const char *version) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi mode %d config %p\n", 52262306a36Sopenharmony_ci mode, config); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ret = ath10k_qmi_cfg_send_sync_msg(ar, config, version); 52562306a36Sopenharmony_ci if (ret) { 52662306a36Sopenharmony_ci ath10k_err(ar, "failed to send qmi config: %d\n", ret); 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = ath10k_qmi_mode_send_sync_msg(ar, mode); 53162306a36Sopenharmony_ci if (ret) { 53262306a36Sopenharmony_ci ath10k_err(ar, "failed to send qmi mode: %d\n", ret); 53362306a36Sopenharmony_ci return ret; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciint ath10k_qmi_wlan_disable(struct ath10k *ar) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void ath10k_qmi_add_wlan_ver_smem(struct ath10k *ar, const char *fw_build_id) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci u8 *table_ptr; 54762306a36Sopenharmony_ci size_t smem_item_size; 54862306a36Sopenharmony_ci const u32 smem_img_idx_wlan = SMEM_IMAGE_TABLE_CNSS_INDEX * 54962306a36Sopenharmony_ci SMEM_IMAGE_VERSION_ENTRY_SIZE; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, 55262306a36Sopenharmony_ci SMEM_IMAGE_VERSION_TABLE, 55362306a36Sopenharmony_ci &smem_item_size); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (IS_ERR(table_ptr)) { 55662306a36Sopenharmony_ci ath10k_err(ar, "smem image version table not found\n"); 55762306a36Sopenharmony_ci return; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (smem_img_idx_wlan + SMEM_IMAGE_VERSION_ENTRY_SIZE > 56162306a36Sopenharmony_ci smem_item_size) { 56262306a36Sopenharmony_ci ath10k_err(ar, "smem block size too small: %zu\n", 56362306a36Sopenharmony_ci smem_item_size); 56462306a36Sopenharmony_ci return; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci strscpy(table_ptr + smem_img_idx_wlan, fw_build_id, 56862306a36Sopenharmony_ci SMEM_IMAGE_VERSION_NAME_SIZE); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct wlfw_cap_resp_msg_v01 *resp; 57462306a36Sopenharmony_ci struct wlfw_cap_req_msg_v01 req = {}; 57562306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 57662306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 57762306a36Sopenharmony_ci struct qmi_txn txn; 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci resp = kzalloc(sizeof(*resp), GFP_KERNEL); 58162306a36Sopenharmony_ci if (!resp) 58262306a36Sopenharmony_ci return -ENOMEM; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cap_resp_msg_v01_ei, resp); 58562306a36Sopenharmony_ci if (ret < 0) 58662306a36Sopenharmony_ci goto out; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 58962306a36Sopenharmony_ci QMI_WLFW_CAP_REQ_V01, 59062306a36Sopenharmony_ci WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN, 59162306a36Sopenharmony_ci wlfw_cap_req_msg_v01_ei, &req); 59262306a36Sopenharmony_ci if (ret < 0) { 59362306a36Sopenharmony_ci qmi_txn_cancel(&txn); 59462306a36Sopenharmony_ci ath10k_err(ar, "failed to send capability request: %d\n", ret); 59562306a36Sopenharmony_ci goto out; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 59962306a36Sopenharmony_ci if (ret < 0) 60062306a36Sopenharmony_ci goto out; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { 60362306a36Sopenharmony_ci ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error); 60462306a36Sopenharmony_ci ret = -EINVAL; 60562306a36Sopenharmony_ci goto out; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (resp->chip_info_valid) { 60962306a36Sopenharmony_ci qmi->chip_info.chip_id = resp->chip_info.chip_id; 61062306a36Sopenharmony_ci qmi->chip_info.chip_family = resp->chip_info.chip_family; 61162306a36Sopenharmony_ci } else { 61262306a36Sopenharmony_ci qmi->chip_info.chip_id = 0xFF; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (resp->board_info_valid) 61662306a36Sopenharmony_ci qmi->board_info.board_id = resp->board_info.board_id; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci qmi->board_info.board_id = 0xFF; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (resp->soc_info_valid) 62162306a36Sopenharmony_ci qmi->soc_info.soc_id = resp->soc_info.soc_id; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (resp->fw_version_info_valid) { 62462306a36Sopenharmony_ci qmi->fw_version = resp->fw_version_info.fw_version; 62562306a36Sopenharmony_ci strscpy(qmi->fw_build_timestamp, resp->fw_version_info.fw_build_timestamp, 62662306a36Sopenharmony_ci sizeof(qmi->fw_build_timestamp)); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (resp->fw_build_id_valid) 63062306a36Sopenharmony_ci strscpy(qmi->fw_build_id, resp->fw_build_id, 63162306a36Sopenharmony_ci MAX_BUILD_ID_LEN + 1); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) { 63462306a36Sopenharmony_ci ath10k_info(ar, "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x", 63562306a36Sopenharmony_ci qmi->chip_info.chip_id, qmi->chip_info.chip_family, 63662306a36Sopenharmony_ci qmi->board_info.board_id, qmi->soc_info.soc_id); 63762306a36Sopenharmony_ci ath10k_info(ar, "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s", 63862306a36Sopenharmony_ci qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (resp->fw_build_id_valid) 64262306a36Sopenharmony_ci ath10k_qmi_add_wlan_ver_smem(ar, qmi->fw_build_id); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci kfree(resp); 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciout: 64862306a36Sopenharmony_ci kfree(resp); 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct wlfw_host_cap_resp_msg_v01 resp = {}; 65562306a36Sopenharmony_ci struct wlfw_host_cap_req_msg_v01 req = {}; 65662306a36Sopenharmony_ci const struct qmi_elem_info *req_ei; 65762306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 65862306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 65962306a36Sopenharmony_ci struct qmi_txn txn; 66062306a36Sopenharmony_ci int ret; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci req.daemon_support_valid = 1; 66362306a36Sopenharmony_ci req.daemon_support = 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_host_cap_resp_msg_v01_ei, 66662306a36Sopenharmony_ci &resp); 66762306a36Sopenharmony_ci if (ret < 0) 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (test_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags)) 67162306a36Sopenharmony_ci req_ei = wlfw_host_cap_8bit_req_msg_v01_ei; 67262306a36Sopenharmony_ci else 67362306a36Sopenharmony_ci req_ei = wlfw_host_cap_req_msg_v01_ei; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 67662306a36Sopenharmony_ci QMI_WLFW_HOST_CAP_REQ_V01, 67762306a36Sopenharmony_ci WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN, 67862306a36Sopenharmony_ci req_ei, &req); 67962306a36Sopenharmony_ci if (ret < 0) { 68062306a36Sopenharmony_ci qmi_txn_cancel(&txn); 68162306a36Sopenharmony_ci ath10k_err(ar, "failed to send host capability request: %d\n", ret); 68262306a36Sopenharmony_ci goto out; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 68662306a36Sopenharmony_ci if (ret < 0) 68762306a36Sopenharmony_ci goto out; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* older FW didn't support this request, which is not fatal */ 69062306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && 69162306a36Sopenharmony_ci resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) { 69262306a36Sopenharmony_ci ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error); 69362306a36Sopenharmony_ci ret = -EINVAL; 69462306a36Sopenharmony_ci goto out; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n"); 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ciout: 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciint ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 70762306a36Sopenharmony_ci struct wlfw_ini_resp_msg_v01 resp = {}; 70862306a36Sopenharmony_ci struct ath10k_qmi *qmi = ar_snoc->qmi; 70962306a36Sopenharmony_ci struct wlfw_ini_req_msg_v01 req = {}; 71062306a36Sopenharmony_ci struct qmi_txn txn; 71162306a36Sopenharmony_ci int ret; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci req.enablefwlog_valid = 1; 71462306a36Sopenharmony_ci req.enablefwlog = fw_log_mode; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_ini_resp_msg_v01_ei, 71762306a36Sopenharmony_ci &resp); 71862306a36Sopenharmony_ci if (ret < 0) 71962306a36Sopenharmony_ci goto out; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 72262306a36Sopenharmony_ci QMI_WLFW_INI_REQ_V01, 72362306a36Sopenharmony_ci WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN, 72462306a36Sopenharmony_ci wlfw_ini_req_msg_v01_ei, &req); 72562306a36Sopenharmony_ci if (ret < 0) { 72662306a36Sopenharmony_ci qmi_txn_cancel(&txn); 72762306a36Sopenharmony_ci ath10k_err(ar, "failed to send fw log request: %d\n", ret); 72862306a36Sopenharmony_ci goto out; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 73262306a36Sopenharmony_ci if (ret < 0) 73362306a36Sopenharmony_ci goto out; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 73662306a36Sopenharmony_ci ath10k_err(ar, "fw log request rejected: %d\n", 73762306a36Sopenharmony_ci resp.resp.error); 73862306a36Sopenharmony_ci ret = -EINVAL; 73962306a36Sopenharmony_ci goto out; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi fw log request completed, mode: %d\n", 74262306a36Sopenharmony_ci fw_log_mode); 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ciout: 74662306a36Sopenharmony_ci return ret; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int 75062306a36Sopenharmony_ciath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct wlfw_ind_register_resp_msg_v01 resp = {}; 75362306a36Sopenharmony_ci struct wlfw_ind_register_req_msg_v01 req = {}; 75462306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 75562306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 75662306a36Sopenharmony_ci struct qmi_txn txn; 75762306a36Sopenharmony_ci int ret; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci req.client_id_valid = 1; 76062306a36Sopenharmony_ci req.client_id = ATH10K_QMI_CLIENT_ID; 76162306a36Sopenharmony_ci req.fw_ready_enable_valid = 1; 76262306a36Sopenharmony_ci req.fw_ready_enable = 1; 76362306a36Sopenharmony_ci req.msa_ready_enable_valid = 1; 76462306a36Sopenharmony_ci req.msa_ready_enable = 1; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (ar_snoc->xo_cal_supported) { 76762306a36Sopenharmony_ci req.xo_cal_enable_valid = 1; 76862306a36Sopenharmony_ci req.xo_cal_enable = 1; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = qmi_txn_init(&qmi->qmi_hdl, &txn, 77262306a36Sopenharmony_ci wlfw_ind_register_resp_msg_v01_ei, &resp); 77362306a36Sopenharmony_ci if (ret < 0) 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, 77762306a36Sopenharmony_ci QMI_WLFW_IND_REGISTER_REQ_V01, 77862306a36Sopenharmony_ci WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN, 77962306a36Sopenharmony_ci wlfw_ind_register_req_msg_v01_ei, &req); 78062306a36Sopenharmony_ci if (ret < 0) { 78162306a36Sopenharmony_ci qmi_txn_cancel(&txn); 78262306a36Sopenharmony_ci ath10k_err(ar, "failed to send indication registered request: %d\n", ret); 78362306a36Sopenharmony_ci goto out; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); 78762306a36Sopenharmony_ci if (ret < 0) 78862306a36Sopenharmony_ci goto out; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 79162306a36Sopenharmony_ci ath10k_err(ar, "indication request rejected: %d\n", resp.resp.error); 79262306a36Sopenharmony_ci ret = -EINVAL; 79362306a36Sopenharmony_ci goto out; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (resp.fw_status_valid) { 79762306a36Sopenharmony_ci if (resp.fw_status & QMI_WLFW_FW_READY_V01) 79862306a36Sopenharmony_ci qmi->fw_ready = true; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi indication register request completed\n"); 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ciout: 80462306a36Sopenharmony_ci return ret; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 81062306a36Sopenharmony_ci int ret; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = ath10k_qmi_ind_register_send_sync_msg(qmi); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci return; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (qmi->fw_ready) { 81762306a36Sopenharmony_ci ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND); 81862306a36Sopenharmony_ci return; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ret = ath10k_qmi_host_cap_send_sync(qmi); 82262306a36Sopenharmony_ci if (ret) 82362306a36Sopenharmony_ci return; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi); 82662306a36Sopenharmony_ci if (ret) 82762306a36Sopenharmony_ci return; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* 83062306a36Sopenharmony_ci * HACK: sleep for a while between receiving the msa info response 83162306a36Sopenharmony_ci * and the XPU update to prevent SDM845 from crashing due to a security 83262306a36Sopenharmony_ci * violation, when running MPSS.AT.4.0.c2-01184-SDM845_GEN_PACK-1. 83362306a36Sopenharmony_ci */ 83462306a36Sopenharmony_ci msleep(20); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ret = ath10k_qmi_setup_msa_permissions(qmi); 83762306a36Sopenharmony_ci if (ret) 83862306a36Sopenharmony_ci return; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci ret = ath10k_qmi_msa_ready_send_sync_msg(qmi); 84162306a36Sopenharmony_ci if (ret) 84262306a36Sopenharmony_ci goto err_setup_msa; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ret = ath10k_qmi_cap_send_sync_msg(qmi); 84562306a36Sopenharmony_ci if (ret) 84662306a36Sopenharmony_ci goto err_setup_msa; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cierr_setup_msa: 85162306a36Sopenharmony_ci ath10k_qmi_remove_msa_permission(qmi); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int ath10k_qmi_fetch_board_file(struct ath10k_qmi *qmi) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 85762306a36Sopenharmony_ci int ret; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ar->hif.bus = ATH10K_BUS_SNOC; 86062306a36Sopenharmony_ci ar->id.qmi_ids_valid = true; 86162306a36Sopenharmony_ci ar->id.qmi_board_id = qmi->board_info.board_id; 86262306a36Sopenharmony_ci ar->id.qmi_chip_id = qmi->chip_info.chip_id; 86362306a36Sopenharmony_ci ar->hw_params.fw.dir = WCN3990_HW_1_0_FW_DIR; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ret = ath10k_core_check_dt(ar); 86662306a36Sopenharmony_ci if (ret) 86762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "DT bdf variant name not set.\n"); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return ath10k_core_fetch_board_file(qmi->ar, ATH10K_BD_IE_BOARD); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int 87362306a36Sopenharmony_ciath10k_qmi_driver_event_post(struct ath10k_qmi *qmi, 87462306a36Sopenharmony_ci enum ath10k_qmi_driver_event_type type, 87562306a36Sopenharmony_ci void *data) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct ath10k_qmi_driver_event *event; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci event = kzalloc(sizeof(*event), GFP_ATOMIC); 88062306a36Sopenharmony_ci if (!event) 88162306a36Sopenharmony_ci return -ENOMEM; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci event->type = type; 88462306a36Sopenharmony_ci event->data = data; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci spin_lock(&qmi->event_lock); 88762306a36Sopenharmony_ci list_add_tail(&event->list, &qmi->event_list); 88862306a36Sopenharmony_ci spin_unlock(&qmi->event_lock); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci queue_work(qmi->event_wq, &qmi->event_work); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 89862306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ath10k_qmi_remove_msa_permission(qmi); 90162306a36Sopenharmony_ci ath10k_core_free_board_files(ar); 90262306a36Sopenharmony_ci if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags) && 90362306a36Sopenharmony_ci !test_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags)) 90462306a36Sopenharmony_ci ath10k_snoc_fw_crashed_dump(ar); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND); 90762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service disconnected\n"); 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void ath10k_qmi_event_msa_ready(struct ath10k_qmi *qmi) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci int ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ret = ath10k_qmi_fetch_board_file(qmi); 91562306a36Sopenharmony_ci if (ret) 91662306a36Sopenharmony_ci goto out; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ret = ath10k_qmi_bdf_dnld_send_sync(qmi); 91962306a36Sopenharmony_ci if (ret) 92062306a36Sopenharmony_ci goto out; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ret = ath10k_qmi_send_cal_report_req(qmi); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciout: 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic int ath10k_qmi_event_fw_ready_ind(struct ath10k_qmi *qmi) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw ready event received\n"); 93362306a36Sopenharmony_ci ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void ath10k_qmi_fw_ready_ind(struct qmi_handle *qmi_hdl, 93962306a36Sopenharmony_ci struct sockaddr_qrtr *sq, 94062306a36Sopenharmony_ci struct qmi_txn *txn, const void *data) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_FW_READY_IND, NULL); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic void ath10k_qmi_msa_ready_ind(struct qmi_handle *qmi_hdl, 94862306a36Sopenharmony_ci struct sockaddr_qrtr *sq, 94962306a36Sopenharmony_ci struct qmi_txn *txn, const void *data) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_MSA_READY_IND, NULL); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const struct qmi_msg_handler qmi_msg_handler[] = { 95762306a36Sopenharmony_ci { 95862306a36Sopenharmony_ci .type = QMI_INDICATION, 95962306a36Sopenharmony_ci .msg_id = QMI_WLFW_FW_READY_IND_V01, 96062306a36Sopenharmony_ci .ei = wlfw_fw_ready_ind_msg_v01_ei, 96162306a36Sopenharmony_ci .decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01), 96262306a36Sopenharmony_ci .fn = ath10k_qmi_fw_ready_ind, 96362306a36Sopenharmony_ci }, 96462306a36Sopenharmony_ci { 96562306a36Sopenharmony_ci .type = QMI_INDICATION, 96662306a36Sopenharmony_ci .msg_id = QMI_WLFW_MSA_READY_IND_V01, 96762306a36Sopenharmony_ci .ei = wlfw_msa_ready_ind_msg_v01_ei, 96862306a36Sopenharmony_ci .decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01), 96962306a36Sopenharmony_ci .fn = ath10k_qmi_msa_ready_ind, 97062306a36Sopenharmony_ci }, 97162306a36Sopenharmony_ci {} 97262306a36Sopenharmony_ci}; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl, 97562306a36Sopenharmony_ci struct qmi_service *service) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl); 97862306a36Sopenharmony_ci struct sockaddr_qrtr *sq = &qmi->sq; 97962306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 98062306a36Sopenharmony_ci int ret; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci sq->sq_family = AF_QIPCRTR; 98362306a36Sopenharmony_ci sq->sq_node = service->node; 98462306a36Sopenharmony_ci sq->sq_port = service->port; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n"); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq, 98962306a36Sopenharmony_ci sizeof(qmi->sq), 0); 99062306a36Sopenharmony_ci if (ret) { 99162306a36Sopenharmony_ci ath10k_err(ar, "failed to connect to a remote QMI service port\n"); 99262306a36Sopenharmony_ci return ret; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wifi fw qmi service connected\n"); 99662306a36Sopenharmony_ci ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_ARRIVE, NULL); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci return ret; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic void ath10k_qmi_del_server(struct qmi_handle *qmi_hdl, 100262306a36Sopenharmony_ci struct qmi_service *service) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct ath10k_qmi *qmi = 100562306a36Sopenharmony_ci container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci qmi->fw_ready = false; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* 101062306a36Sopenharmony_ci * The del_server event is to be processed only if coming from 101162306a36Sopenharmony_ci * the qmi server. The qmi infrastructure sends del_server, when 101262306a36Sopenharmony_ci * any client releases the qmi handle. In this case do not process 101362306a36Sopenharmony_ci * this del_server event. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ci if (qmi->state == ATH10K_QMI_STATE_INIT_DONE) 101662306a36Sopenharmony_ci ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_EXIT, 101762306a36Sopenharmony_ci NULL); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic const struct qmi_ops ath10k_qmi_ops = { 102162306a36Sopenharmony_ci .new_server = ath10k_qmi_new_server, 102262306a36Sopenharmony_ci .del_server = ath10k_qmi_del_server, 102362306a36Sopenharmony_ci}; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic void ath10k_qmi_driver_event_work(struct work_struct *work) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct ath10k_qmi *qmi = container_of(work, struct ath10k_qmi, 102862306a36Sopenharmony_ci event_work); 102962306a36Sopenharmony_ci struct ath10k_qmi_driver_event *event; 103062306a36Sopenharmony_ci struct ath10k *ar = qmi->ar; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci spin_lock(&qmi->event_lock); 103362306a36Sopenharmony_ci while (!list_empty(&qmi->event_list)) { 103462306a36Sopenharmony_ci event = list_first_entry(&qmi->event_list, 103562306a36Sopenharmony_ci struct ath10k_qmi_driver_event, list); 103662306a36Sopenharmony_ci list_del(&event->list); 103762306a36Sopenharmony_ci spin_unlock(&qmi->event_lock); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci switch (event->type) { 104062306a36Sopenharmony_ci case ATH10K_QMI_EVENT_SERVER_ARRIVE: 104162306a36Sopenharmony_ci ath10k_qmi_event_server_arrive(qmi); 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci case ATH10K_QMI_EVENT_SERVER_EXIT: 104462306a36Sopenharmony_ci ath10k_qmi_event_server_exit(qmi); 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci case ATH10K_QMI_EVENT_FW_READY_IND: 104762306a36Sopenharmony_ci ath10k_qmi_event_fw_ready_ind(qmi); 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci case ATH10K_QMI_EVENT_MSA_READY_IND: 105062306a36Sopenharmony_ci ath10k_qmi_event_msa_ready(qmi); 105162306a36Sopenharmony_ci break; 105262306a36Sopenharmony_ci default: 105362306a36Sopenharmony_ci ath10k_warn(ar, "invalid event type: %d", event->type); 105462306a36Sopenharmony_ci break; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci kfree(event); 105762306a36Sopenharmony_ci spin_lock(&qmi->event_lock); 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci spin_unlock(&qmi->event_lock); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ciint ath10k_qmi_init(struct ath10k *ar, u32 msa_size) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 106562306a36Sopenharmony_ci struct device *dev = ar->dev; 106662306a36Sopenharmony_ci struct ath10k_qmi *qmi; 106762306a36Sopenharmony_ci int ret; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci qmi = kzalloc(sizeof(*qmi), GFP_KERNEL); 107062306a36Sopenharmony_ci if (!qmi) 107162306a36Sopenharmony_ci return -ENOMEM; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci qmi->ar = ar; 107462306a36Sopenharmony_ci ar_snoc->qmi = qmi; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm")) 107762306a36Sopenharmony_ci qmi->msa_fixed_perm = true; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci ret = qmi_handle_init(&qmi->qmi_hdl, 108062306a36Sopenharmony_ci WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN, 108162306a36Sopenharmony_ci &ath10k_qmi_ops, qmi_msg_handler); 108262306a36Sopenharmony_ci if (ret) 108362306a36Sopenharmony_ci goto err; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci qmi->event_wq = alloc_ordered_workqueue("ath10k_qmi_driver_event", 0); 108662306a36Sopenharmony_ci if (!qmi->event_wq) { 108762306a36Sopenharmony_ci ath10k_err(ar, "failed to allocate workqueue\n"); 108862306a36Sopenharmony_ci ret = -EFAULT; 108962306a36Sopenharmony_ci goto err_release_qmi_handle; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci INIT_LIST_HEAD(&qmi->event_list); 109362306a36Sopenharmony_ci spin_lock_init(&qmi->event_lock); 109462306a36Sopenharmony_ci INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01, 109762306a36Sopenharmony_ci WLFW_SERVICE_VERS_V01, 0); 109862306a36Sopenharmony_ci if (ret) 109962306a36Sopenharmony_ci goto err_qmi_lookup; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci qmi->state = ATH10K_QMI_STATE_INIT_DONE; 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cierr_qmi_lookup: 110562306a36Sopenharmony_ci destroy_workqueue(qmi->event_wq); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cierr_release_qmi_handle: 110862306a36Sopenharmony_ci qmi_handle_release(&qmi->qmi_hdl); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cierr: 111162306a36Sopenharmony_ci kfree(qmi); 111262306a36Sopenharmony_ci return ret; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ciint ath10k_qmi_deinit(struct ath10k *ar) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); 111862306a36Sopenharmony_ci struct ath10k_qmi *qmi = ar_snoc->qmi; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci qmi->state = ATH10K_QMI_STATE_DEINIT; 112162306a36Sopenharmony_ci qmi_handle_release(&qmi->qmi_hdl); 112262306a36Sopenharmony_ci cancel_work_sync(&qmi->event_work); 112362306a36Sopenharmony_ci destroy_workqueue(qmi->event_wq); 112462306a36Sopenharmony_ci kfree(qmi); 112562306a36Sopenharmony_ci ar_snoc->qmi = NULL; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return 0; 112862306a36Sopenharmony_ci} 1129