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