18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018 The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/completion.h>
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/idr.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_address.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/net.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <net/sock.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "debug.h"
218c2ecf20Sopenharmony_ci#include "snoc.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define ATH10K_QMI_CLIENT_ID		0x4b4e454c
248c2ecf20Sopenharmony_ci#define ATH10K_QMI_TIMEOUT		30
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,
278c2ecf20Sopenharmony_ci					 struct ath10k_msa_mem_info *mem_info)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct qcom_scm_vmperm dst_perms[3];
308c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
318c2ecf20Sopenharmony_ci	unsigned int src_perms;
328c2ecf20Sopenharmony_ci	u32 perm_count;
338c2ecf20Sopenharmony_ci	int ret;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	src_perms = BIT(QCOM_SCM_VMID_HLOS);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	dst_perms[0].vmid = QCOM_SCM_VMID_MSS_MSA;
388c2ecf20Sopenharmony_ci	dst_perms[0].perm = QCOM_SCM_PERM_RW;
398c2ecf20Sopenharmony_ci	dst_perms[1].vmid = QCOM_SCM_VMID_WLAN;
408c2ecf20Sopenharmony_ci	dst_perms[1].perm = QCOM_SCM_PERM_RW;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (mem_info->secure) {
438c2ecf20Sopenharmony_ci		perm_count = 2;
448c2ecf20Sopenharmony_ci	} else {
458c2ecf20Sopenharmony_ci		dst_perms[2].vmid = QCOM_SCM_VMID_WLAN_CE;
468c2ecf20Sopenharmony_ci		dst_perms[2].perm = QCOM_SCM_PERM_RW;
478c2ecf20Sopenharmony_ci		perm_count = 3;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
518c2ecf20Sopenharmony_ci				  &src_perms, dst_perms, perm_count);
528c2ecf20Sopenharmony_ci	if (ret < 0)
538c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to assign msa map permissions: %d\n", ret);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return ret;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi,
598c2ecf20Sopenharmony_ci					   struct ath10k_msa_mem_info *mem_info)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct qcom_scm_vmperm dst_perms;
628c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
638c2ecf20Sopenharmony_ci	unsigned int src_perms;
648c2ecf20Sopenharmony_ci	int ret;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (!mem_info->secure)
698c2ecf20Sopenharmony_ci		src_perms |= BIT(QCOM_SCM_VMID_WLAN_CE);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	dst_perms.vmid = QCOM_SCM_VMID_HLOS;
728c2ecf20Sopenharmony_ci	dst_perms.perm = QCOM_SCM_PERM_RW;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
758c2ecf20Sopenharmony_ci				  &src_perms, &dst_perms, 1);
768c2ecf20Sopenharmony_ci	if (ret < 0)
778c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to unmap msa permissions: %d\n", ret);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return ret;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci	int i;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (qmi->msa_fixed_perm)
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	for (i = 0; i < qmi->nr_mem_region; i++) {
918c2ecf20Sopenharmony_ci		ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]);
928c2ecf20Sopenharmony_ci		if (ret)
938c2ecf20Sopenharmony_ci			goto err_unmap;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cierr_unmap:
998c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
1008c2ecf20Sopenharmony_ci		ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
1018c2ecf20Sopenharmony_ci	return ret;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (qmi->msa_fixed_perm)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	for (i = 0; i < qmi->nr_mem_region; i++)
1128c2ecf20Sopenharmony_ci		ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi *qmi)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct wlfw_msa_info_resp_msg_v01 resp = {};
1188c2ecf20Sopenharmony_ci	struct wlfw_msa_info_req_msg_v01 req = {};
1198c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
1208c2ecf20Sopenharmony_ci	phys_addr_t max_mapped_addr;
1218c2ecf20Sopenharmony_ci	struct qmi_txn txn;
1228c2ecf20Sopenharmony_ci	int ret;
1238c2ecf20Sopenharmony_ci	int i;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	req.msa_addr = ar->msa.paddr;
1268c2ecf20Sopenharmony_ci	req.size = ar->msa.mem_size;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
1298c2ecf20Sopenharmony_ci			   wlfw_msa_info_resp_msg_v01_ei, &resp);
1308c2ecf20Sopenharmony_ci	if (ret < 0)
1318c2ecf20Sopenharmony_ci		goto out;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
1348c2ecf20Sopenharmony_ci			       QMI_WLFW_MSA_INFO_REQ_V01,
1358c2ecf20Sopenharmony_ci			       WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,
1368c2ecf20Sopenharmony_ci			       wlfw_msa_info_req_msg_v01_ei, &req);
1378c2ecf20Sopenharmony_ci	if (ret < 0) {
1388c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
1398c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send msa mem info req: %d\n", ret);
1408c2ecf20Sopenharmony_ci		goto out;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
1448c2ecf20Sopenharmony_ci	if (ret < 0)
1458c2ecf20Sopenharmony_ci		goto out;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1488c2ecf20Sopenharmony_ci		ath10k_err(ar, "msa info req rejected: %d\n", resp.resp.error);
1498c2ecf20Sopenharmony_ci		ret = -EINVAL;
1508c2ecf20Sopenharmony_ci		goto out;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (resp.mem_region_info_len > QMI_WLFW_MAX_MEM_REG_V01) {
1548c2ecf20Sopenharmony_ci		ath10k_err(ar, "invalid memory region length received: %d\n",
1558c2ecf20Sopenharmony_ci			   resp.mem_region_info_len);
1568c2ecf20Sopenharmony_ci		ret = -EINVAL;
1578c2ecf20Sopenharmony_ci		goto out;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	max_mapped_addr = ar->msa.paddr + ar->msa.mem_size;
1618c2ecf20Sopenharmony_ci	qmi->nr_mem_region = resp.mem_region_info_len;
1628c2ecf20Sopenharmony_ci	for (i = 0; i < resp.mem_region_info_len; i++) {
1638c2ecf20Sopenharmony_ci		if (resp.mem_region_info[i].size > ar->msa.mem_size ||
1648c2ecf20Sopenharmony_ci		    resp.mem_region_info[i].region_addr > max_mapped_addr ||
1658c2ecf20Sopenharmony_ci		    resp.mem_region_info[i].region_addr < ar->msa.paddr ||
1668c2ecf20Sopenharmony_ci		    resp.mem_region_info[i].size +
1678c2ecf20Sopenharmony_ci		    resp.mem_region_info[i].region_addr > max_mapped_addr) {
1688c2ecf20Sopenharmony_ci			ath10k_err(ar, "received out of range memory region address 0x%llx with size 0x%x, aborting\n",
1698c2ecf20Sopenharmony_ci				   resp.mem_region_info[i].region_addr,
1708c2ecf20Sopenharmony_ci				   resp.mem_region_info[i].size);
1718c2ecf20Sopenharmony_ci			ret = -EINVAL;
1728c2ecf20Sopenharmony_ci			goto fail_unwind;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci		qmi->mem_region[i].addr = resp.mem_region_info[i].region_addr;
1758c2ecf20Sopenharmony_ci		qmi->mem_region[i].size = resp.mem_region_info[i].size;
1768c2ecf20Sopenharmony_ci		qmi->mem_region[i].secure = resp.mem_region_info[i].secure_flag;
1778c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_QMI,
1788c2ecf20Sopenharmony_ci			   "qmi msa mem region %d addr 0x%pa size 0x%x flag 0x%08x\n",
1798c2ecf20Sopenharmony_ci			   i, &qmi->mem_region[i].addr,
1808c2ecf20Sopenharmony_ci			   qmi->mem_region[i].size,
1818c2ecf20Sopenharmony_ci			   qmi->mem_region[i].secure);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem info request completed\n");
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cifail_unwind:
1888c2ecf20Sopenharmony_ci	memset(&qmi->mem_region[0], 0, sizeof(qmi->mem_region[0]) * i);
1898c2ecf20Sopenharmony_ciout:
1908c2ecf20Sopenharmony_ci	return ret;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int ath10k_qmi_msa_ready_send_sync_msg(struct ath10k_qmi *qmi)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct wlfw_msa_ready_resp_msg_v01 resp = {};
1968c2ecf20Sopenharmony_ci	struct wlfw_msa_ready_req_msg_v01 req = {};
1978c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
1988c2ecf20Sopenharmony_ci	struct qmi_txn txn;
1998c2ecf20Sopenharmony_ci	int ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
2028c2ecf20Sopenharmony_ci			   wlfw_msa_ready_resp_msg_v01_ei, &resp);
2038c2ecf20Sopenharmony_ci	if (ret < 0)
2048c2ecf20Sopenharmony_ci		goto out;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
2078c2ecf20Sopenharmony_ci			       QMI_WLFW_MSA_READY_REQ_V01,
2088c2ecf20Sopenharmony_ci			       WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN,
2098c2ecf20Sopenharmony_ci			       wlfw_msa_ready_req_msg_v01_ei, &req);
2108c2ecf20Sopenharmony_ci	if (ret < 0) {
2118c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
2128c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send msa mem ready request: %d\n", ret);
2138c2ecf20Sopenharmony_ci		goto out;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
2178c2ecf20Sopenharmony_ci	if (ret < 0)
2188c2ecf20Sopenharmony_ci		goto out;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
2218c2ecf20Sopenharmony_ci		ath10k_err(ar, "msa ready request rejected: %d\n", resp.resp.error);
2228c2ecf20Sopenharmony_ci		ret = -EINVAL;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem ready request completed\n");
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciout:
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct wlfw_bdf_download_resp_msg_v01 resp = {};
2358c2ecf20Sopenharmony_ci	struct wlfw_bdf_download_req_msg_v01 *req;
2368c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
2378c2ecf20Sopenharmony_ci	unsigned int remaining;
2388c2ecf20Sopenharmony_ci	struct qmi_txn txn;
2398c2ecf20Sopenharmony_ci	const u8 *temp;
2408c2ecf20Sopenharmony_ci	int ret;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
2438c2ecf20Sopenharmony_ci	if (!req)
2448c2ecf20Sopenharmony_ci		return -ENOMEM;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	temp = ar->normal_mode_fw.board_data;
2478c2ecf20Sopenharmony_ci	remaining = ar->normal_mode_fw.board_len;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	while (remaining) {
2508c2ecf20Sopenharmony_ci		req->valid = 1;
2518c2ecf20Sopenharmony_ci		req->file_id_valid = 1;
2528c2ecf20Sopenharmony_ci		req->file_id = 0;
2538c2ecf20Sopenharmony_ci		req->total_size_valid = 1;
2548c2ecf20Sopenharmony_ci		req->total_size = ar->normal_mode_fw.board_len;
2558c2ecf20Sopenharmony_ci		req->seg_id_valid = 1;
2568c2ecf20Sopenharmony_ci		req->data_valid = 1;
2578c2ecf20Sopenharmony_ci		req->end_valid = 1;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
2608c2ecf20Sopenharmony_ci			req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
2618c2ecf20Sopenharmony_ci		} else {
2628c2ecf20Sopenharmony_ci			req->data_len = remaining;
2638c2ecf20Sopenharmony_ci			req->end = 1;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		memcpy(req->data, temp, req->data_len);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
2698c2ecf20Sopenharmony_ci				   wlfw_bdf_download_resp_msg_v01_ei,
2708c2ecf20Sopenharmony_ci				   &resp);
2718c2ecf20Sopenharmony_ci		if (ret < 0)
2728c2ecf20Sopenharmony_ci			goto out;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
2758c2ecf20Sopenharmony_ci				       QMI_WLFW_BDF_DOWNLOAD_REQ_V01,
2768c2ecf20Sopenharmony_ci				       WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
2778c2ecf20Sopenharmony_ci				       wlfw_bdf_download_req_msg_v01_ei, req);
2788c2ecf20Sopenharmony_ci		if (ret < 0) {
2798c2ecf20Sopenharmony_ci			qmi_txn_cancel(&txn);
2808c2ecf20Sopenharmony_ci			goto out;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		if (ret < 0)
2868c2ecf20Sopenharmony_ci			goto out;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		/* end = 1 triggers a CRC check on the BDF.  If this fails, we
2898c2ecf20Sopenharmony_ci		 * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still
2908c2ecf20Sopenharmony_ci		 * willing to use the BDF.  For some platforms, all the valid
2918c2ecf20Sopenharmony_ci		 * released BDFs fail this CRC check, so attempt to detect this
2928c2ecf20Sopenharmony_ci		 * scenario and treat it as non-fatal.
2938c2ecf20Sopenharmony_ci		 */
2948c2ecf20Sopenharmony_ci		if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&
2958c2ecf20Sopenharmony_ci		    !(req->end == 1 &&
2968c2ecf20Sopenharmony_ci		      resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) {
2978c2ecf20Sopenharmony_ci			ath10k_err(ar, "failed to download board data file: %d\n",
2988c2ecf20Sopenharmony_ci				   resp.resp.error);
2998c2ecf20Sopenharmony_ci			ret = -EINVAL;
3008c2ecf20Sopenharmony_ci			goto out;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		remaining -= req->data_len;
3048c2ecf20Sopenharmony_ci		temp += req->data_len;
3058c2ecf20Sopenharmony_ci		req->seg_id++;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi bdf download request completed\n");
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	kfree(req);
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ciout:
3148c2ecf20Sopenharmony_ci	kfree(req);
3158c2ecf20Sopenharmony_ci	return ret;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int ath10k_qmi_send_cal_report_req(struct ath10k_qmi *qmi)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct wlfw_cal_report_resp_msg_v01 resp = {};
3218c2ecf20Sopenharmony_ci	struct wlfw_cal_report_req_msg_v01 req = {};
3228c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
3238c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
3248c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3258c2ecf20Sopenharmony_ci	int i, j = 0;
3268c2ecf20Sopenharmony_ci	int ret;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (ar_snoc->xo_cal_supported) {
3298c2ecf20Sopenharmony_ci		req.xo_cal_data_valid = 1;
3308c2ecf20Sopenharmony_ci		req.xo_cal_data = ar_snoc->xo_cal_data;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cal_report_resp_msg_v01_ei,
3348c2ecf20Sopenharmony_ci			   &resp);
3358c2ecf20Sopenharmony_ci	if (ret < 0)
3368c2ecf20Sopenharmony_ci		goto out;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (i = 0; i < QMI_WLFW_MAX_NUM_CAL_V01; i++) {
3398c2ecf20Sopenharmony_ci		if (qmi->cal_data[i].total_size &&
3408c2ecf20Sopenharmony_ci		    qmi->cal_data[i].data) {
3418c2ecf20Sopenharmony_ci			req.meta_data[j] = qmi->cal_data[i].cal_id;
3428c2ecf20Sopenharmony_ci			j++;
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	req.meta_data_len = j;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
3488c2ecf20Sopenharmony_ci			       QMI_WLFW_CAL_REPORT_REQ_V01,
3498c2ecf20Sopenharmony_ci			       WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN,
3508c2ecf20Sopenharmony_ci			       wlfw_cal_report_req_msg_v01_ei, &req);
3518c2ecf20Sopenharmony_ci	if (ret < 0) {
3528c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
3538c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send calibration request: %d\n", ret);
3548c2ecf20Sopenharmony_ci		goto out;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
3588c2ecf20Sopenharmony_ci	if (ret < 0)
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
3628c2ecf20Sopenharmony_ci		ath10k_err(ar, "calibration request rejected: %d\n", resp.resp.error);
3638c2ecf20Sopenharmony_ci		ret = -EINVAL;
3648c2ecf20Sopenharmony_ci		goto out;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi cal report request completed\n");
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciout:
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int
3758c2ecf20Sopenharmony_ciath10k_qmi_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
3788c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = ar_snoc->qmi;
3798c2ecf20Sopenharmony_ci	struct wlfw_wlan_mode_resp_msg_v01 resp = {};
3808c2ecf20Sopenharmony_ci	struct wlfw_wlan_mode_req_msg_v01 req = {};
3818c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3828c2ecf20Sopenharmony_ci	int ret;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
3858c2ecf20Sopenharmony_ci			   wlfw_wlan_mode_resp_msg_v01_ei,
3868c2ecf20Sopenharmony_ci			   &resp);
3878c2ecf20Sopenharmony_ci	if (ret < 0)
3888c2ecf20Sopenharmony_ci		goto out;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	req.mode = mode;
3918c2ecf20Sopenharmony_ci	req.hw_debug_valid = 1;
3928c2ecf20Sopenharmony_ci	req.hw_debug = 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
3958c2ecf20Sopenharmony_ci			       QMI_WLFW_WLAN_MODE_REQ_V01,
3968c2ecf20Sopenharmony_ci			       WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN,
3978c2ecf20Sopenharmony_ci			       wlfw_wlan_mode_req_msg_v01_ei, &req);
3988c2ecf20Sopenharmony_ci	if (ret < 0) {
3998c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
4008c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send wlan mode %d request: %d\n", mode, ret);
4018c2ecf20Sopenharmony_ci		goto out;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
4058c2ecf20Sopenharmony_ci	if (ret < 0)
4068c2ecf20Sopenharmony_ci		goto out;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
4098c2ecf20Sopenharmony_ci		ath10k_err(ar, "more request rejected: %d\n", resp.resp.error);
4108c2ecf20Sopenharmony_ci		ret = -EINVAL;
4118c2ecf20Sopenharmony_ci		goto out;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wlan mode req completed: %d\n", mode);
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciout:
4188c2ecf20Sopenharmony_ci	return ret;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int
4228c2ecf20Sopenharmony_ciath10k_qmi_cfg_send_sync_msg(struct ath10k *ar,
4238c2ecf20Sopenharmony_ci			     struct ath10k_qmi_wlan_enable_cfg *config,
4248c2ecf20Sopenharmony_ci			     const char *version)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
4278c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = ar_snoc->qmi;
4288c2ecf20Sopenharmony_ci	struct wlfw_wlan_cfg_resp_msg_v01 resp = {};
4298c2ecf20Sopenharmony_ci	struct wlfw_wlan_cfg_req_msg_v01 *req;
4308c2ecf20Sopenharmony_ci	struct qmi_txn txn;
4318c2ecf20Sopenharmony_ci	int ret;
4328c2ecf20Sopenharmony_ci	u32 i;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
4358c2ecf20Sopenharmony_ci	if (!req)
4368c2ecf20Sopenharmony_ci		return -ENOMEM;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
4398c2ecf20Sopenharmony_ci			   wlfw_wlan_cfg_resp_msg_v01_ei,
4408c2ecf20Sopenharmony_ci			   &resp);
4418c2ecf20Sopenharmony_ci	if (ret < 0)
4428c2ecf20Sopenharmony_ci		goto out;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	req->host_version_valid = 0;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	req->tgt_cfg_valid = 1;
4478c2ecf20Sopenharmony_ci	if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
4488c2ecf20Sopenharmony_ci		req->tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
4498c2ecf20Sopenharmony_ci	else
4508c2ecf20Sopenharmony_ci		req->tgt_cfg_len = config->num_ce_tgt_cfg;
4518c2ecf20Sopenharmony_ci	for (i = 0; i < req->tgt_cfg_len; i++) {
4528c2ecf20Sopenharmony_ci		req->tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
4538c2ecf20Sopenharmony_ci		req->tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
4548c2ecf20Sopenharmony_ci		req->tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
4558c2ecf20Sopenharmony_ci		req->tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
4568c2ecf20Sopenharmony_ci		req->tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	req->svc_cfg_valid = 1;
4608c2ecf20Sopenharmony_ci	if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
4618c2ecf20Sopenharmony_ci		req->svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
4628c2ecf20Sopenharmony_ci	else
4638c2ecf20Sopenharmony_ci		req->svc_cfg_len = config->num_ce_svc_pipe_cfg;
4648c2ecf20Sopenharmony_ci	for (i = 0; i < req->svc_cfg_len; i++) {
4658c2ecf20Sopenharmony_ci		req->svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
4668c2ecf20Sopenharmony_ci		req->svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
4678c2ecf20Sopenharmony_ci		req->svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	req->shadow_reg_valid = 1;
4718c2ecf20Sopenharmony_ci	if (config->num_shadow_reg_cfg >
4728c2ecf20Sopenharmony_ci	    QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
4738c2ecf20Sopenharmony_ci		req->shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
4748c2ecf20Sopenharmony_ci	else
4758c2ecf20Sopenharmony_ci		req->shadow_reg_len = config->num_shadow_reg_cfg;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	memcpy(req->shadow_reg, config->shadow_reg_cfg,
4788c2ecf20Sopenharmony_ci	       sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req->shadow_reg_len);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
4818c2ecf20Sopenharmony_ci			       QMI_WLFW_WLAN_CFG_REQ_V01,
4828c2ecf20Sopenharmony_ci			       WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN,
4838c2ecf20Sopenharmony_ci			       wlfw_wlan_cfg_req_msg_v01_ei, req);
4848c2ecf20Sopenharmony_ci	if (ret < 0) {
4858c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
4868c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send config request: %d\n", ret);
4878c2ecf20Sopenharmony_ci		goto out;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
4918c2ecf20Sopenharmony_ci	if (ret < 0)
4928c2ecf20Sopenharmony_ci		goto out;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
4958c2ecf20Sopenharmony_ci		ath10k_err(ar, "config request rejected: %d\n", resp.resp.error);
4968c2ecf20Sopenharmony_ci		ret = -EINVAL;
4978c2ecf20Sopenharmony_ci		goto out;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi config request completed\n");
5018c2ecf20Sopenharmony_ci	kfree(req);
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ciout:
5058c2ecf20Sopenharmony_ci	kfree(req);
5068c2ecf20Sopenharmony_ci	return ret;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ciint ath10k_qmi_wlan_enable(struct ath10k *ar,
5108c2ecf20Sopenharmony_ci			   struct ath10k_qmi_wlan_enable_cfg *config,
5118c2ecf20Sopenharmony_ci			   enum wlfw_driver_mode_enum_v01 mode,
5128c2ecf20Sopenharmony_ci			   const char *version)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	int ret;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi mode %d config %p\n",
5178c2ecf20Sopenharmony_ci		   mode, config);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	ret = ath10k_qmi_cfg_send_sync_msg(ar, config, version);
5208c2ecf20Sopenharmony_ci	if (ret) {
5218c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send qmi config: %d\n", ret);
5228c2ecf20Sopenharmony_ci		return ret;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	ret = ath10k_qmi_mode_send_sync_msg(ar, mode);
5268c2ecf20Sopenharmony_ci	if (ret) {
5278c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send qmi mode: %d\n", ret);
5288c2ecf20Sopenharmony_ci		return ret;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return 0;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ciint ath10k_qmi_wlan_disable(struct ath10k *ar)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct wlfw_cap_resp_msg_v01 *resp;
5428c2ecf20Sopenharmony_ci	struct wlfw_cap_req_msg_v01 req = {};
5438c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
5448c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
5458c2ecf20Sopenharmony_ci	struct qmi_txn txn;
5468c2ecf20Sopenharmony_ci	int ret;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
5498c2ecf20Sopenharmony_ci	if (!resp)
5508c2ecf20Sopenharmony_ci		return -ENOMEM;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cap_resp_msg_v01_ei, resp);
5538c2ecf20Sopenharmony_ci	if (ret < 0)
5548c2ecf20Sopenharmony_ci		goto out;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
5578c2ecf20Sopenharmony_ci			       QMI_WLFW_CAP_REQ_V01,
5588c2ecf20Sopenharmony_ci			       WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN,
5598c2ecf20Sopenharmony_ci			       wlfw_cap_req_msg_v01_ei, &req);
5608c2ecf20Sopenharmony_ci	if (ret < 0) {
5618c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
5628c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send capability request: %d\n", ret);
5638c2ecf20Sopenharmony_ci		goto out;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
5678c2ecf20Sopenharmony_ci	if (ret < 0)
5688c2ecf20Sopenharmony_ci		goto out;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
5718c2ecf20Sopenharmony_ci		ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error);
5728c2ecf20Sopenharmony_ci		ret = -EINVAL;
5738c2ecf20Sopenharmony_ci		goto out;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (resp->chip_info_valid) {
5778c2ecf20Sopenharmony_ci		qmi->chip_info.chip_id = resp->chip_info.chip_id;
5788c2ecf20Sopenharmony_ci		qmi->chip_info.chip_family = resp->chip_info.chip_family;
5798c2ecf20Sopenharmony_ci	} else {
5808c2ecf20Sopenharmony_ci		qmi->chip_info.chip_id = 0xFF;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (resp->board_info_valid)
5848c2ecf20Sopenharmony_ci		qmi->board_info.board_id = resp->board_info.board_id;
5858c2ecf20Sopenharmony_ci	else
5868c2ecf20Sopenharmony_ci		qmi->board_info.board_id = 0xFF;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (resp->soc_info_valid)
5898c2ecf20Sopenharmony_ci		qmi->soc_info.soc_id = resp->soc_info.soc_id;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (resp->fw_version_info_valid) {
5928c2ecf20Sopenharmony_ci		qmi->fw_version = resp->fw_version_info.fw_version;
5938c2ecf20Sopenharmony_ci		strlcpy(qmi->fw_build_timestamp, resp->fw_version_info.fw_build_timestamp,
5948c2ecf20Sopenharmony_ci			sizeof(qmi->fw_build_timestamp));
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (resp->fw_build_id_valid)
5988c2ecf20Sopenharmony_ci		strlcpy(qmi->fw_build_id, resp->fw_build_id,
5998c2ecf20Sopenharmony_ci			MAX_BUILD_ID_LEN + 1);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (!test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) {
6028c2ecf20Sopenharmony_ci		ath10k_info(ar, "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x",
6038c2ecf20Sopenharmony_ci			    qmi->chip_info.chip_id, qmi->chip_info.chip_family,
6048c2ecf20Sopenharmony_ci			    qmi->board_info.board_id, qmi->soc_info.soc_id);
6058c2ecf20Sopenharmony_ci		ath10k_info(ar, "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s",
6068c2ecf20Sopenharmony_ci			    qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id);
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	kfree(resp);
6108c2ecf20Sopenharmony_ci	return 0;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ciout:
6138c2ecf20Sopenharmony_ci	kfree(resp);
6148c2ecf20Sopenharmony_ci	return ret;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct wlfw_host_cap_resp_msg_v01 resp = {};
6208c2ecf20Sopenharmony_ci	struct wlfw_host_cap_req_msg_v01 req = {};
6218c2ecf20Sopenharmony_ci	struct qmi_elem_info *req_ei;
6228c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
6238c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
6248c2ecf20Sopenharmony_ci	struct qmi_txn txn;
6258c2ecf20Sopenharmony_ci	int ret;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	req.daemon_support_valid = 1;
6288c2ecf20Sopenharmony_ci	req.daemon_support = 0;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_host_cap_resp_msg_v01_ei,
6318c2ecf20Sopenharmony_ci			   &resp);
6328c2ecf20Sopenharmony_ci	if (ret < 0)
6338c2ecf20Sopenharmony_ci		goto out;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (test_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags))
6368c2ecf20Sopenharmony_ci		req_ei = wlfw_host_cap_8bit_req_msg_v01_ei;
6378c2ecf20Sopenharmony_ci	else
6388c2ecf20Sopenharmony_ci		req_ei = wlfw_host_cap_req_msg_v01_ei;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
6418c2ecf20Sopenharmony_ci			       QMI_WLFW_HOST_CAP_REQ_V01,
6428c2ecf20Sopenharmony_ci			       WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,
6438c2ecf20Sopenharmony_ci			       req_ei, &req);
6448c2ecf20Sopenharmony_ci	if (ret < 0) {
6458c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
6468c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send host capability request: %d\n", ret);
6478c2ecf20Sopenharmony_ci		goto out;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
6518c2ecf20Sopenharmony_ci	if (ret < 0)
6528c2ecf20Sopenharmony_ci		goto out;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* older FW didn't support this request, which is not fatal */
6558c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&
6568c2ecf20Sopenharmony_ci	    resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) {
6578c2ecf20Sopenharmony_ci		ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error);
6588c2ecf20Sopenharmony_ci		ret = -EINVAL;
6598c2ecf20Sopenharmony_ci		goto out;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n");
6638c2ecf20Sopenharmony_ci	return 0;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ciout:
6668c2ecf20Sopenharmony_ci	return ret;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciint ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
6728c2ecf20Sopenharmony_ci	struct wlfw_ini_resp_msg_v01 resp = {};
6738c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = ar_snoc->qmi;
6748c2ecf20Sopenharmony_ci	struct wlfw_ini_req_msg_v01 req = {};
6758c2ecf20Sopenharmony_ci	struct qmi_txn txn;
6768c2ecf20Sopenharmony_ci	int ret;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	req.enablefwlog_valid = 1;
6798c2ecf20Sopenharmony_ci	req.enablefwlog = fw_log_mode;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_ini_resp_msg_v01_ei,
6828c2ecf20Sopenharmony_ci			   &resp);
6838c2ecf20Sopenharmony_ci	if (ret < 0)
6848c2ecf20Sopenharmony_ci		goto out;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
6878c2ecf20Sopenharmony_ci			       QMI_WLFW_INI_REQ_V01,
6888c2ecf20Sopenharmony_ci			       WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,
6898c2ecf20Sopenharmony_ci			       wlfw_ini_req_msg_v01_ei, &req);
6908c2ecf20Sopenharmony_ci	if (ret < 0) {
6918c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
6928c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send fw log request: %d\n", ret);
6938c2ecf20Sopenharmony_ci		goto out;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
6978c2ecf20Sopenharmony_ci	if (ret < 0)
6988c2ecf20Sopenharmony_ci		goto out;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
7018c2ecf20Sopenharmony_ci		ath10k_err(ar, "fw log request rejected: %d\n",
7028c2ecf20Sopenharmony_ci			   resp.resp.error);
7038c2ecf20Sopenharmony_ci		ret = -EINVAL;
7048c2ecf20Sopenharmony_ci		goto out;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi fw log request completed, mode: %d\n",
7078c2ecf20Sopenharmony_ci		   fw_log_mode);
7088c2ecf20Sopenharmony_ci	return 0;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ciout:
7118c2ecf20Sopenharmony_ci	return ret;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic int
7158c2ecf20Sopenharmony_ciath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct wlfw_ind_register_resp_msg_v01 resp = {};
7188c2ecf20Sopenharmony_ci	struct wlfw_ind_register_req_msg_v01 req = {};
7198c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
7208c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
7218c2ecf20Sopenharmony_ci	struct qmi_txn txn;
7228c2ecf20Sopenharmony_ci	int ret;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	req.client_id_valid = 1;
7258c2ecf20Sopenharmony_ci	req.client_id = ATH10K_QMI_CLIENT_ID;
7268c2ecf20Sopenharmony_ci	req.fw_ready_enable_valid = 1;
7278c2ecf20Sopenharmony_ci	req.fw_ready_enable = 1;
7288c2ecf20Sopenharmony_ci	req.msa_ready_enable_valid = 1;
7298c2ecf20Sopenharmony_ci	req.msa_ready_enable = 1;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (ar_snoc->xo_cal_supported) {
7328c2ecf20Sopenharmony_ci		req.xo_cal_enable_valid = 1;
7338c2ecf20Sopenharmony_ci		req.xo_cal_enable = 1;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
7378c2ecf20Sopenharmony_ci			   wlfw_ind_register_resp_msg_v01_ei, &resp);
7388c2ecf20Sopenharmony_ci	if (ret < 0)
7398c2ecf20Sopenharmony_ci		goto out;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
7428c2ecf20Sopenharmony_ci			       QMI_WLFW_IND_REGISTER_REQ_V01,
7438c2ecf20Sopenharmony_ci			       WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN,
7448c2ecf20Sopenharmony_ci			       wlfw_ind_register_req_msg_v01_ei, &req);
7458c2ecf20Sopenharmony_ci	if (ret < 0) {
7468c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
7478c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to send indication registered request: %d\n", ret);
7488c2ecf20Sopenharmony_ci		goto out;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
7528c2ecf20Sopenharmony_ci	if (ret < 0)
7538c2ecf20Sopenharmony_ci		goto out;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
7568c2ecf20Sopenharmony_ci		ath10k_err(ar, "indication request rejected: %d\n", resp.resp.error);
7578c2ecf20Sopenharmony_ci		ret = -EINVAL;
7588c2ecf20Sopenharmony_ci		goto out;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (resp.fw_status_valid) {
7628c2ecf20Sopenharmony_ci		if (resp.fw_status & QMI_WLFW_FW_READY_V01)
7638c2ecf20Sopenharmony_ci			qmi->fw_ready = true;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi indication register request completed\n");
7668c2ecf20Sopenharmony_ci	return 0;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciout:
7698c2ecf20Sopenharmony_ci	return ret;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
7758c2ecf20Sopenharmony_ci	int ret;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	ret = ath10k_qmi_ind_register_send_sync_msg(qmi);
7788c2ecf20Sopenharmony_ci	if (ret)
7798c2ecf20Sopenharmony_ci		return;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (qmi->fw_ready) {
7828c2ecf20Sopenharmony_ci		ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);
7838c2ecf20Sopenharmony_ci		return;
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	ret = ath10k_qmi_host_cap_send_sync(qmi);
7878c2ecf20Sopenharmony_ci	if (ret)
7888c2ecf20Sopenharmony_ci		return;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi);
7918c2ecf20Sopenharmony_ci	if (ret)
7928c2ecf20Sopenharmony_ci		return;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/*
7958c2ecf20Sopenharmony_ci	 * HACK: sleep for a while inbetween receiving the msa info response
7968c2ecf20Sopenharmony_ci	 * and the XPU update to prevent SDM845 from crashing due to a security
7978c2ecf20Sopenharmony_ci	 * violation, when running MPSS.AT.4.0.c2-01184-SDM845_GEN_PACK-1.
7988c2ecf20Sopenharmony_ci	 */
7998c2ecf20Sopenharmony_ci	msleep(20);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	ret = ath10k_qmi_setup_msa_permissions(qmi);
8028c2ecf20Sopenharmony_ci	if (ret)
8038c2ecf20Sopenharmony_ci		return;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	ret = ath10k_qmi_msa_ready_send_sync_msg(qmi);
8068c2ecf20Sopenharmony_ci	if (ret)
8078c2ecf20Sopenharmony_ci		goto err_setup_msa;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	ret = ath10k_qmi_cap_send_sync_msg(qmi);
8108c2ecf20Sopenharmony_ci	if (ret)
8118c2ecf20Sopenharmony_ci		goto err_setup_msa;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	return;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cierr_setup_msa:
8168c2ecf20Sopenharmony_ci	ath10k_qmi_remove_msa_permission(qmi);
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int ath10k_qmi_fetch_board_file(struct ath10k_qmi *qmi)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
8228c2ecf20Sopenharmony_ci	int ret;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	ar->hif.bus = ATH10K_BUS_SNOC;
8258c2ecf20Sopenharmony_ci	ar->id.qmi_ids_valid = true;
8268c2ecf20Sopenharmony_ci	ar->id.qmi_board_id = qmi->board_info.board_id;
8278c2ecf20Sopenharmony_ci	ar->id.qmi_chip_id = qmi->chip_info.chip_id;
8288c2ecf20Sopenharmony_ci	ar->hw_params.fw.dir = WCN3990_HW_1_0_FW_DIR;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	ret = ath10k_core_check_dt(ar);
8318c2ecf20Sopenharmony_ci	if (ret)
8328c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_QMI, "DT bdf variant name not set.\n");
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	return ath10k_core_fetch_board_file(qmi->ar, ATH10K_BD_IE_BOARD);
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic int
8388c2ecf20Sopenharmony_ciath10k_qmi_driver_event_post(struct ath10k_qmi *qmi,
8398c2ecf20Sopenharmony_ci			     enum ath10k_qmi_driver_event_type type,
8408c2ecf20Sopenharmony_ci			     void *data)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct ath10k_qmi_driver_event *event;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	event = kzalloc(sizeof(*event), GFP_ATOMIC);
8458c2ecf20Sopenharmony_ci	if (!event)
8468c2ecf20Sopenharmony_ci		return -ENOMEM;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	event->type = type;
8498c2ecf20Sopenharmony_ci	event->data = data;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	spin_lock(&qmi->event_lock);
8528c2ecf20Sopenharmony_ci	list_add_tail(&event->list, &qmi->event_list);
8538c2ecf20Sopenharmony_ci	spin_unlock(&qmi->event_lock);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	queue_work(qmi->event_wq, &qmi->event_work);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	return 0;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
8638c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	ath10k_qmi_remove_msa_permission(qmi);
8668c2ecf20Sopenharmony_ci	ath10k_core_free_board_files(ar);
8678c2ecf20Sopenharmony_ci	if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags))
8688c2ecf20Sopenharmony_ci		ath10k_snoc_fw_crashed_dump(ar);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND);
8718c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service disconnected\n");
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic void ath10k_qmi_event_msa_ready(struct ath10k_qmi *qmi)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	int ret;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	ret = ath10k_qmi_fetch_board_file(qmi);
8798c2ecf20Sopenharmony_ci	if (ret)
8808c2ecf20Sopenharmony_ci		goto out;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	ret = ath10k_qmi_bdf_dnld_send_sync(qmi);
8838c2ecf20Sopenharmony_ci	if (ret)
8848c2ecf20Sopenharmony_ci		goto out;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	ret = ath10k_qmi_send_cal_report_req(qmi);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ciout:
8898c2ecf20Sopenharmony_ci	return;
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic int ath10k_qmi_event_fw_ready_ind(struct ath10k_qmi *qmi)
8938c2ecf20Sopenharmony_ci{
8948c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw ready event received\n");
8978c2ecf20Sopenharmony_ci	ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	return 0;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic void ath10k_qmi_fw_ready_ind(struct qmi_handle *qmi_hdl,
9038c2ecf20Sopenharmony_ci				    struct sockaddr_qrtr *sq,
9048c2ecf20Sopenharmony_ci				    struct qmi_txn *txn, const void *data)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_FW_READY_IND, NULL);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void ath10k_qmi_msa_ready_ind(struct qmi_handle *qmi_hdl,
9128c2ecf20Sopenharmony_ci				     struct sockaddr_qrtr *sq,
9138c2ecf20Sopenharmony_ci				     struct qmi_txn *txn, const void *data)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_MSA_READY_IND, NULL);
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic struct qmi_msg_handler qmi_msg_handler[] = {
9218c2ecf20Sopenharmony_ci	{
9228c2ecf20Sopenharmony_ci		.type = QMI_INDICATION,
9238c2ecf20Sopenharmony_ci		.msg_id = QMI_WLFW_FW_READY_IND_V01,
9248c2ecf20Sopenharmony_ci		.ei = wlfw_fw_ready_ind_msg_v01_ei,
9258c2ecf20Sopenharmony_ci		.decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01),
9268c2ecf20Sopenharmony_ci		.fn = ath10k_qmi_fw_ready_ind,
9278c2ecf20Sopenharmony_ci	},
9288c2ecf20Sopenharmony_ci	{
9298c2ecf20Sopenharmony_ci		.type = QMI_INDICATION,
9308c2ecf20Sopenharmony_ci		.msg_id = QMI_WLFW_MSA_READY_IND_V01,
9318c2ecf20Sopenharmony_ci		.ei = wlfw_msa_ready_ind_msg_v01_ei,
9328c2ecf20Sopenharmony_ci		.decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01),
9338c2ecf20Sopenharmony_ci		.fn = ath10k_qmi_msa_ready_ind,
9348c2ecf20Sopenharmony_ci	},
9358c2ecf20Sopenharmony_ci	{}
9368c2ecf20Sopenharmony_ci};
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_cistatic int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,
9398c2ecf20Sopenharmony_ci				 struct qmi_service *service)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
9428c2ecf20Sopenharmony_ci	struct sockaddr_qrtr *sq = &qmi->sq;
9438c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
9448c2ecf20Sopenharmony_ci	int ret;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	sq->sq_family = AF_QIPCRTR;
9478c2ecf20Sopenharmony_ci	sq->sq_node = service->node;
9488c2ecf20Sopenharmony_ci	sq->sq_port = service->port;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq,
9538c2ecf20Sopenharmony_ci			     sizeof(qmi->sq), 0);
9548c2ecf20Sopenharmony_ci	if (ret) {
9558c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to connect to a remote QMI service port\n");
9568c2ecf20Sopenharmony_ci		return ret;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wifi fw qmi service connected\n");
9608c2ecf20Sopenharmony_ci	ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_ARRIVE, NULL);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	return ret;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic void ath10k_qmi_del_server(struct qmi_handle *qmi_hdl,
9668c2ecf20Sopenharmony_ci				  struct qmi_service *service)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi =
9698c2ecf20Sopenharmony_ci		container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	qmi->fw_ready = false;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/*
9748c2ecf20Sopenharmony_ci	 * The del_server event is to be processed only if coming from
9758c2ecf20Sopenharmony_ci	 * the qmi server. The qmi infrastructure sends del_server, when
9768c2ecf20Sopenharmony_ci	 * any client releases the qmi handle. In this case do not process
9778c2ecf20Sopenharmony_ci	 * this del_server event.
9788c2ecf20Sopenharmony_ci	 */
9798c2ecf20Sopenharmony_ci	if (qmi->state == ATH10K_QMI_STATE_INIT_DONE)
9808c2ecf20Sopenharmony_ci		ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_EXIT,
9818c2ecf20Sopenharmony_ci					     NULL);
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic struct qmi_ops ath10k_qmi_ops = {
9858c2ecf20Sopenharmony_ci	.new_server = ath10k_qmi_new_server,
9868c2ecf20Sopenharmony_ci	.del_server = ath10k_qmi_del_server,
9878c2ecf20Sopenharmony_ci};
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic void ath10k_qmi_driver_event_work(struct work_struct *work)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = container_of(work, struct ath10k_qmi,
9928c2ecf20Sopenharmony_ci					      event_work);
9938c2ecf20Sopenharmony_ci	struct ath10k_qmi_driver_event *event;
9948c2ecf20Sopenharmony_ci	struct ath10k *ar = qmi->ar;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	spin_lock(&qmi->event_lock);
9978c2ecf20Sopenharmony_ci	while (!list_empty(&qmi->event_list)) {
9988c2ecf20Sopenharmony_ci		event = list_first_entry(&qmi->event_list,
9998c2ecf20Sopenharmony_ci					 struct ath10k_qmi_driver_event, list);
10008c2ecf20Sopenharmony_ci		list_del(&event->list);
10018c2ecf20Sopenharmony_ci		spin_unlock(&qmi->event_lock);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci		switch (event->type) {
10048c2ecf20Sopenharmony_ci		case ATH10K_QMI_EVENT_SERVER_ARRIVE:
10058c2ecf20Sopenharmony_ci			ath10k_qmi_event_server_arrive(qmi);
10068c2ecf20Sopenharmony_ci			break;
10078c2ecf20Sopenharmony_ci		case ATH10K_QMI_EVENT_SERVER_EXIT:
10088c2ecf20Sopenharmony_ci			ath10k_qmi_event_server_exit(qmi);
10098c2ecf20Sopenharmony_ci			break;
10108c2ecf20Sopenharmony_ci		case ATH10K_QMI_EVENT_FW_READY_IND:
10118c2ecf20Sopenharmony_ci			ath10k_qmi_event_fw_ready_ind(qmi);
10128c2ecf20Sopenharmony_ci			break;
10138c2ecf20Sopenharmony_ci		case ATH10K_QMI_EVENT_MSA_READY_IND:
10148c2ecf20Sopenharmony_ci			ath10k_qmi_event_msa_ready(qmi);
10158c2ecf20Sopenharmony_ci			break;
10168c2ecf20Sopenharmony_ci		default:
10178c2ecf20Sopenharmony_ci			ath10k_warn(ar, "invalid event type: %d", event->type);
10188c2ecf20Sopenharmony_ci			break;
10198c2ecf20Sopenharmony_ci		}
10208c2ecf20Sopenharmony_ci		kfree(event);
10218c2ecf20Sopenharmony_ci		spin_lock(&qmi->event_lock);
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci	spin_unlock(&qmi->event_lock);
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ciint ath10k_qmi_init(struct ath10k *ar, u32 msa_size)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
10298c2ecf20Sopenharmony_ci	struct device *dev = ar->dev;
10308c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi;
10318c2ecf20Sopenharmony_ci	int ret;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	qmi = kzalloc(sizeof(*qmi), GFP_KERNEL);
10348c2ecf20Sopenharmony_ci	if (!qmi)
10358c2ecf20Sopenharmony_ci		return -ENOMEM;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	qmi->ar = ar;
10388c2ecf20Sopenharmony_ci	ar_snoc->qmi = qmi;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm"))
10418c2ecf20Sopenharmony_ci		qmi->msa_fixed_perm = true;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	ret = qmi_handle_init(&qmi->qmi_hdl,
10448c2ecf20Sopenharmony_ci			      WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
10458c2ecf20Sopenharmony_ci			      &ath10k_qmi_ops, qmi_msg_handler);
10468c2ecf20Sopenharmony_ci	if (ret)
10478c2ecf20Sopenharmony_ci		goto err;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	qmi->event_wq = alloc_workqueue("ath10k_qmi_driver_event",
10508c2ecf20Sopenharmony_ci					WQ_UNBOUND, 1);
10518c2ecf20Sopenharmony_ci	if (!qmi->event_wq) {
10528c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to allocate workqueue\n");
10538c2ecf20Sopenharmony_ci		ret = -EFAULT;
10548c2ecf20Sopenharmony_ci		goto err_release_qmi_handle;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&qmi->event_list);
10588c2ecf20Sopenharmony_ci	spin_lock_init(&qmi->event_lock);
10598c2ecf20Sopenharmony_ci	INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01,
10628c2ecf20Sopenharmony_ci			     WLFW_SERVICE_VERS_V01, 0);
10638c2ecf20Sopenharmony_ci	if (ret)
10648c2ecf20Sopenharmony_ci		goto err_qmi_lookup;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	qmi->state = ATH10K_QMI_STATE_INIT_DONE;
10678c2ecf20Sopenharmony_ci	return 0;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_cierr_qmi_lookup:
10708c2ecf20Sopenharmony_ci	destroy_workqueue(qmi->event_wq);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cierr_release_qmi_handle:
10738c2ecf20Sopenharmony_ci	qmi_handle_release(&qmi->qmi_hdl);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_cierr:
10768c2ecf20Sopenharmony_ci	kfree(qmi);
10778c2ecf20Sopenharmony_ci	return ret;
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ciint ath10k_qmi_deinit(struct ath10k *ar)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
10838c2ecf20Sopenharmony_ci	struct ath10k_qmi *qmi = ar_snoc->qmi;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	qmi->state = ATH10K_QMI_STATE_DEINIT;
10868c2ecf20Sopenharmony_ci	qmi_handle_release(&qmi->qmi_hdl);
10878c2ecf20Sopenharmony_ci	cancel_work_sync(&qmi->event_work);
10888c2ecf20Sopenharmony_ci	destroy_workqueue(qmi->event_wq);
10898c2ecf20Sopenharmony_ci	kfree(qmi);
10908c2ecf20Sopenharmony_ci	ar_snoc->qmi = NULL;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	return 0;
10938c2ecf20Sopenharmony_ci}
1094