1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2018 The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/completion.h>
7*4882a593Smuzhiyun #include <linux/device.h>
8*4882a593Smuzhiyun #include <linux/debugfs.h>
9*4882a593Smuzhiyun #include <linux/idr.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/of.h>
12*4882a593Smuzhiyun #include <linux/of_address.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/net.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/qcom_scm.h>
17*4882a593Smuzhiyun #include <linux/string.h>
18*4882a593Smuzhiyun #include <net/sock.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "debug.h"
21*4882a593Smuzhiyun #include "snoc.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define ATH10K_QMI_CLIENT_ID 0x4b4e454c
24*4882a593Smuzhiyun #define ATH10K_QMI_TIMEOUT 30
25*4882a593Smuzhiyun
ath10k_qmi_map_msa_permission(struct ath10k_qmi * qmi,struct ath10k_msa_mem_info * mem_info)26*4882a593Smuzhiyun static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,
27*4882a593Smuzhiyun struct ath10k_msa_mem_info *mem_info)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun struct qcom_scm_vmperm dst_perms[3];
30*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
31*4882a593Smuzhiyun unsigned int src_perms;
32*4882a593Smuzhiyun u32 perm_count;
33*4882a593Smuzhiyun int ret;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun src_perms = BIT(QCOM_SCM_VMID_HLOS);
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun dst_perms[0].vmid = QCOM_SCM_VMID_MSS_MSA;
38*4882a593Smuzhiyun dst_perms[0].perm = QCOM_SCM_PERM_RW;
39*4882a593Smuzhiyun dst_perms[1].vmid = QCOM_SCM_VMID_WLAN;
40*4882a593Smuzhiyun dst_perms[1].perm = QCOM_SCM_PERM_RW;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun if (mem_info->secure) {
43*4882a593Smuzhiyun perm_count = 2;
44*4882a593Smuzhiyun } else {
45*4882a593Smuzhiyun dst_perms[2].vmid = QCOM_SCM_VMID_WLAN_CE;
46*4882a593Smuzhiyun dst_perms[2].perm = QCOM_SCM_PERM_RW;
47*4882a593Smuzhiyun perm_count = 3;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
51*4882a593Smuzhiyun &src_perms, dst_perms, perm_count);
52*4882a593Smuzhiyun if (ret < 0)
53*4882a593Smuzhiyun ath10k_err(ar, "failed to assign msa map permissions: %d\n", ret);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun return ret;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
ath10k_qmi_unmap_msa_permission(struct ath10k_qmi * qmi,struct ath10k_msa_mem_info * mem_info)58*4882a593Smuzhiyun static int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi,
59*4882a593Smuzhiyun struct ath10k_msa_mem_info *mem_info)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun struct qcom_scm_vmperm dst_perms;
62*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
63*4882a593Smuzhiyun unsigned int src_perms;
64*4882a593Smuzhiyun int ret;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN);
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun if (!mem_info->secure)
69*4882a593Smuzhiyun src_perms |= BIT(QCOM_SCM_VMID_WLAN_CE);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun dst_perms.vmid = QCOM_SCM_VMID_HLOS;
72*4882a593Smuzhiyun dst_perms.perm = QCOM_SCM_PERM_RW;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
75*4882a593Smuzhiyun &src_perms, &dst_perms, 1);
76*4882a593Smuzhiyun if (ret < 0)
77*4882a593Smuzhiyun ath10k_err(ar, "failed to unmap msa permissions: %d\n", ret);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun return ret;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
ath10k_qmi_setup_msa_permissions(struct ath10k_qmi * qmi)82*4882a593Smuzhiyun static int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun int ret;
85*4882a593Smuzhiyun int i;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (qmi->msa_fixed_perm)
88*4882a593Smuzhiyun return 0;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun for (i = 0; i < qmi->nr_mem_region; i++) {
91*4882a593Smuzhiyun ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]);
92*4882a593Smuzhiyun if (ret)
93*4882a593Smuzhiyun goto err_unmap;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun err_unmap:
99*4882a593Smuzhiyun for (i--; i >= 0; i--)
100*4882a593Smuzhiyun ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
101*4882a593Smuzhiyun return ret;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
ath10k_qmi_remove_msa_permission(struct ath10k_qmi * qmi)104*4882a593Smuzhiyun static void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun int i;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (qmi->msa_fixed_perm)
109*4882a593Smuzhiyun return;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun for (i = 0; i < qmi->nr_mem_region; i++)
112*4882a593Smuzhiyun ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi * qmi)115*4882a593Smuzhiyun static int ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi *qmi)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun struct wlfw_msa_info_resp_msg_v01 resp = {};
118*4882a593Smuzhiyun struct wlfw_msa_info_req_msg_v01 req = {};
119*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
120*4882a593Smuzhiyun phys_addr_t max_mapped_addr;
121*4882a593Smuzhiyun struct qmi_txn txn;
122*4882a593Smuzhiyun int ret;
123*4882a593Smuzhiyun int i;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun req.msa_addr = ar->msa.paddr;
126*4882a593Smuzhiyun req.size = ar->msa.mem_size;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
129*4882a593Smuzhiyun wlfw_msa_info_resp_msg_v01_ei, &resp);
130*4882a593Smuzhiyun if (ret < 0)
131*4882a593Smuzhiyun goto out;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
134*4882a593Smuzhiyun QMI_WLFW_MSA_INFO_REQ_V01,
135*4882a593Smuzhiyun WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,
136*4882a593Smuzhiyun wlfw_msa_info_req_msg_v01_ei, &req);
137*4882a593Smuzhiyun if (ret < 0) {
138*4882a593Smuzhiyun qmi_txn_cancel(&txn);
139*4882a593Smuzhiyun ath10k_err(ar, "failed to send msa mem info req: %d\n", ret);
140*4882a593Smuzhiyun goto out;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
144*4882a593Smuzhiyun if (ret < 0)
145*4882a593Smuzhiyun goto out;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
148*4882a593Smuzhiyun ath10k_err(ar, "msa info req rejected: %d\n", resp.resp.error);
149*4882a593Smuzhiyun ret = -EINVAL;
150*4882a593Smuzhiyun goto out;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (resp.mem_region_info_len > QMI_WLFW_MAX_MEM_REG_V01) {
154*4882a593Smuzhiyun ath10k_err(ar, "invalid memory region length received: %d\n",
155*4882a593Smuzhiyun resp.mem_region_info_len);
156*4882a593Smuzhiyun ret = -EINVAL;
157*4882a593Smuzhiyun goto out;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun max_mapped_addr = ar->msa.paddr + ar->msa.mem_size;
161*4882a593Smuzhiyun qmi->nr_mem_region = resp.mem_region_info_len;
162*4882a593Smuzhiyun for (i = 0; i < resp.mem_region_info_len; i++) {
163*4882a593Smuzhiyun if (resp.mem_region_info[i].size > ar->msa.mem_size ||
164*4882a593Smuzhiyun resp.mem_region_info[i].region_addr > max_mapped_addr ||
165*4882a593Smuzhiyun resp.mem_region_info[i].region_addr < ar->msa.paddr ||
166*4882a593Smuzhiyun resp.mem_region_info[i].size +
167*4882a593Smuzhiyun resp.mem_region_info[i].region_addr > max_mapped_addr) {
168*4882a593Smuzhiyun ath10k_err(ar, "received out of range memory region address 0x%llx with size 0x%x, aborting\n",
169*4882a593Smuzhiyun resp.mem_region_info[i].region_addr,
170*4882a593Smuzhiyun resp.mem_region_info[i].size);
171*4882a593Smuzhiyun ret = -EINVAL;
172*4882a593Smuzhiyun goto fail_unwind;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun qmi->mem_region[i].addr = resp.mem_region_info[i].region_addr;
175*4882a593Smuzhiyun qmi->mem_region[i].size = resp.mem_region_info[i].size;
176*4882a593Smuzhiyun qmi->mem_region[i].secure = resp.mem_region_info[i].secure_flag;
177*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI,
178*4882a593Smuzhiyun "qmi msa mem region %d addr 0x%pa size 0x%x flag 0x%08x\n",
179*4882a593Smuzhiyun i, &qmi->mem_region[i].addr,
180*4882a593Smuzhiyun qmi->mem_region[i].size,
181*4882a593Smuzhiyun qmi->mem_region[i].secure);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem info request completed\n");
185*4882a593Smuzhiyun return 0;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun fail_unwind:
188*4882a593Smuzhiyun memset(&qmi->mem_region[0], 0, sizeof(qmi->mem_region[0]) * i);
189*4882a593Smuzhiyun out:
190*4882a593Smuzhiyun return ret;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
ath10k_qmi_msa_ready_send_sync_msg(struct ath10k_qmi * qmi)193*4882a593Smuzhiyun static int ath10k_qmi_msa_ready_send_sync_msg(struct ath10k_qmi *qmi)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun struct wlfw_msa_ready_resp_msg_v01 resp = {};
196*4882a593Smuzhiyun struct wlfw_msa_ready_req_msg_v01 req = {};
197*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
198*4882a593Smuzhiyun struct qmi_txn txn;
199*4882a593Smuzhiyun int ret;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
202*4882a593Smuzhiyun wlfw_msa_ready_resp_msg_v01_ei, &resp);
203*4882a593Smuzhiyun if (ret < 0)
204*4882a593Smuzhiyun goto out;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
207*4882a593Smuzhiyun QMI_WLFW_MSA_READY_REQ_V01,
208*4882a593Smuzhiyun WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN,
209*4882a593Smuzhiyun wlfw_msa_ready_req_msg_v01_ei, &req);
210*4882a593Smuzhiyun if (ret < 0) {
211*4882a593Smuzhiyun qmi_txn_cancel(&txn);
212*4882a593Smuzhiyun ath10k_err(ar, "failed to send msa mem ready request: %d\n", ret);
213*4882a593Smuzhiyun goto out;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
217*4882a593Smuzhiyun if (ret < 0)
218*4882a593Smuzhiyun goto out;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
221*4882a593Smuzhiyun ath10k_err(ar, "msa ready request rejected: %d\n", resp.resp.error);
222*4882a593Smuzhiyun ret = -EINVAL;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem ready request completed\n");
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun out:
229*4882a593Smuzhiyun return ret;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi * qmi)232*4882a593Smuzhiyun static int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun struct wlfw_bdf_download_resp_msg_v01 resp = {};
235*4882a593Smuzhiyun struct wlfw_bdf_download_req_msg_v01 *req;
236*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
237*4882a593Smuzhiyun unsigned int remaining;
238*4882a593Smuzhiyun struct qmi_txn txn;
239*4882a593Smuzhiyun const u8 *temp;
240*4882a593Smuzhiyun int ret;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun req = kzalloc(sizeof(*req), GFP_KERNEL);
243*4882a593Smuzhiyun if (!req)
244*4882a593Smuzhiyun return -ENOMEM;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun temp = ar->normal_mode_fw.board_data;
247*4882a593Smuzhiyun remaining = ar->normal_mode_fw.board_len;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun while (remaining) {
250*4882a593Smuzhiyun req->valid = 1;
251*4882a593Smuzhiyun req->file_id_valid = 1;
252*4882a593Smuzhiyun req->file_id = 0;
253*4882a593Smuzhiyun req->total_size_valid = 1;
254*4882a593Smuzhiyun req->total_size = ar->normal_mode_fw.board_len;
255*4882a593Smuzhiyun req->seg_id_valid = 1;
256*4882a593Smuzhiyun req->data_valid = 1;
257*4882a593Smuzhiyun req->end_valid = 1;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
260*4882a593Smuzhiyun req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
261*4882a593Smuzhiyun } else {
262*4882a593Smuzhiyun req->data_len = remaining;
263*4882a593Smuzhiyun req->end = 1;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun memcpy(req->data, temp, req->data_len);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
269*4882a593Smuzhiyun wlfw_bdf_download_resp_msg_v01_ei,
270*4882a593Smuzhiyun &resp);
271*4882a593Smuzhiyun if (ret < 0)
272*4882a593Smuzhiyun goto out;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
275*4882a593Smuzhiyun QMI_WLFW_BDF_DOWNLOAD_REQ_V01,
276*4882a593Smuzhiyun WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
277*4882a593Smuzhiyun wlfw_bdf_download_req_msg_v01_ei, req);
278*4882a593Smuzhiyun if (ret < 0) {
279*4882a593Smuzhiyun qmi_txn_cancel(&txn);
280*4882a593Smuzhiyun goto out;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun if (ret < 0)
286*4882a593Smuzhiyun goto out;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun /* end = 1 triggers a CRC check on the BDF. If this fails, we
289*4882a593Smuzhiyun * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still
290*4882a593Smuzhiyun * willing to use the BDF. For some platforms, all the valid
291*4882a593Smuzhiyun * released BDFs fail this CRC check, so attempt to detect this
292*4882a593Smuzhiyun * scenario and treat it as non-fatal.
293*4882a593Smuzhiyun */
294*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&
295*4882a593Smuzhiyun !(req->end == 1 &&
296*4882a593Smuzhiyun resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) {
297*4882a593Smuzhiyun ath10k_err(ar, "failed to download board data file: %d\n",
298*4882a593Smuzhiyun resp.resp.error);
299*4882a593Smuzhiyun ret = -EINVAL;
300*4882a593Smuzhiyun goto out;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun remaining -= req->data_len;
304*4882a593Smuzhiyun temp += req->data_len;
305*4882a593Smuzhiyun req->seg_id++;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi bdf download request completed\n");
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun kfree(req);
311*4882a593Smuzhiyun return 0;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun out:
314*4882a593Smuzhiyun kfree(req);
315*4882a593Smuzhiyun return ret;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
ath10k_qmi_send_cal_report_req(struct ath10k_qmi * qmi)318*4882a593Smuzhiyun static int ath10k_qmi_send_cal_report_req(struct ath10k_qmi *qmi)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun struct wlfw_cal_report_resp_msg_v01 resp = {};
321*4882a593Smuzhiyun struct wlfw_cal_report_req_msg_v01 req = {};
322*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
323*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
324*4882a593Smuzhiyun struct qmi_txn txn;
325*4882a593Smuzhiyun int i, j = 0;
326*4882a593Smuzhiyun int ret;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun if (ar_snoc->xo_cal_supported) {
329*4882a593Smuzhiyun req.xo_cal_data_valid = 1;
330*4882a593Smuzhiyun req.xo_cal_data = ar_snoc->xo_cal_data;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cal_report_resp_msg_v01_ei,
334*4882a593Smuzhiyun &resp);
335*4882a593Smuzhiyun if (ret < 0)
336*4882a593Smuzhiyun goto out;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun for (i = 0; i < QMI_WLFW_MAX_NUM_CAL_V01; i++) {
339*4882a593Smuzhiyun if (qmi->cal_data[i].total_size &&
340*4882a593Smuzhiyun qmi->cal_data[i].data) {
341*4882a593Smuzhiyun req.meta_data[j] = qmi->cal_data[i].cal_id;
342*4882a593Smuzhiyun j++;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun req.meta_data_len = j;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
348*4882a593Smuzhiyun QMI_WLFW_CAL_REPORT_REQ_V01,
349*4882a593Smuzhiyun WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN,
350*4882a593Smuzhiyun wlfw_cal_report_req_msg_v01_ei, &req);
351*4882a593Smuzhiyun if (ret < 0) {
352*4882a593Smuzhiyun qmi_txn_cancel(&txn);
353*4882a593Smuzhiyun ath10k_err(ar, "failed to send calibration request: %d\n", ret);
354*4882a593Smuzhiyun goto out;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
358*4882a593Smuzhiyun if (ret < 0)
359*4882a593Smuzhiyun goto out;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
362*4882a593Smuzhiyun ath10k_err(ar, "calibration request rejected: %d\n", resp.resp.error);
363*4882a593Smuzhiyun ret = -EINVAL;
364*4882a593Smuzhiyun goto out;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi cal report request completed\n");
368*4882a593Smuzhiyun return 0;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun out:
371*4882a593Smuzhiyun return ret;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun static int
ath10k_qmi_mode_send_sync_msg(struct ath10k * ar,enum wlfw_driver_mode_enum_v01 mode)375*4882a593Smuzhiyun ath10k_qmi_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
378*4882a593Smuzhiyun struct ath10k_qmi *qmi = ar_snoc->qmi;
379*4882a593Smuzhiyun struct wlfw_wlan_mode_resp_msg_v01 resp = {};
380*4882a593Smuzhiyun struct wlfw_wlan_mode_req_msg_v01 req = {};
381*4882a593Smuzhiyun struct qmi_txn txn;
382*4882a593Smuzhiyun int ret;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
385*4882a593Smuzhiyun wlfw_wlan_mode_resp_msg_v01_ei,
386*4882a593Smuzhiyun &resp);
387*4882a593Smuzhiyun if (ret < 0)
388*4882a593Smuzhiyun goto out;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun req.mode = mode;
391*4882a593Smuzhiyun req.hw_debug_valid = 1;
392*4882a593Smuzhiyun req.hw_debug = 0;
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
395*4882a593Smuzhiyun QMI_WLFW_WLAN_MODE_REQ_V01,
396*4882a593Smuzhiyun WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN,
397*4882a593Smuzhiyun wlfw_wlan_mode_req_msg_v01_ei, &req);
398*4882a593Smuzhiyun if (ret < 0) {
399*4882a593Smuzhiyun qmi_txn_cancel(&txn);
400*4882a593Smuzhiyun ath10k_err(ar, "failed to send wlan mode %d request: %d\n", mode, ret);
401*4882a593Smuzhiyun goto out;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
405*4882a593Smuzhiyun if (ret < 0)
406*4882a593Smuzhiyun goto out;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
409*4882a593Smuzhiyun ath10k_err(ar, "more request rejected: %d\n", resp.resp.error);
410*4882a593Smuzhiyun ret = -EINVAL;
411*4882a593Smuzhiyun goto out;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wlan mode req completed: %d\n", mode);
415*4882a593Smuzhiyun return 0;
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun out:
418*4882a593Smuzhiyun return ret;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun static int
ath10k_qmi_cfg_send_sync_msg(struct ath10k * ar,struct ath10k_qmi_wlan_enable_cfg * config,const char * version)422*4882a593Smuzhiyun ath10k_qmi_cfg_send_sync_msg(struct ath10k *ar,
423*4882a593Smuzhiyun struct ath10k_qmi_wlan_enable_cfg *config,
424*4882a593Smuzhiyun const char *version)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
427*4882a593Smuzhiyun struct ath10k_qmi *qmi = ar_snoc->qmi;
428*4882a593Smuzhiyun struct wlfw_wlan_cfg_resp_msg_v01 resp = {};
429*4882a593Smuzhiyun struct wlfw_wlan_cfg_req_msg_v01 *req;
430*4882a593Smuzhiyun struct qmi_txn txn;
431*4882a593Smuzhiyun int ret;
432*4882a593Smuzhiyun u32 i;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun req = kzalloc(sizeof(*req), GFP_KERNEL);
435*4882a593Smuzhiyun if (!req)
436*4882a593Smuzhiyun return -ENOMEM;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
439*4882a593Smuzhiyun wlfw_wlan_cfg_resp_msg_v01_ei,
440*4882a593Smuzhiyun &resp);
441*4882a593Smuzhiyun if (ret < 0)
442*4882a593Smuzhiyun goto out;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun req->host_version_valid = 0;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun req->tgt_cfg_valid = 1;
447*4882a593Smuzhiyun if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
448*4882a593Smuzhiyun req->tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
449*4882a593Smuzhiyun else
450*4882a593Smuzhiyun req->tgt_cfg_len = config->num_ce_tgt_cfg;
451*4882a593Smuzhiyun for (i = 0; i < req->tgt_cfg_len; i++) {
452*4882a593Smuzhiyun req->tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
453*4882a593Smuzhiyun req->tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
454*4882a593Smuzhiyun req->tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
455*4882a593Smuzhiyun req->tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
456*4882a593Smuzhiyun req->tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun req->svc_cfg_valid = 1;
460*4882a593Smuzhiyun if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
461*4882a593Smuzhiyun req->svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
462*4882a593Smuzhiyun else
463*4882a593Smuzhiyun req->svc_cfg_len = config->num_ce_svc_pipe_cfg;
464*4882a593Smuzhiyun for (i = 0; i < req->svc_cfg_len; i++) {
465*4882a593Smuzhiyun req->svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
466*4882a593Smuzhiyun req->svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
467*4882a593Smuzhiyun req->svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun req->shadow_reg_valid = 1;
471*4882a593Smuzhiyun if (config->num_shadow_reg_cfg >
472*4882a593Smuzhiyun QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
473*4882a593Smuzhiyun req->shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
474*4882a593Smuzhiyun else
475*4882a593Smuzhiyun req->shadow_reg_len = config->num_shadow_reg_cfg;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun memcpy(req->shadow_reg, config->shadow_reg_cfg,
478*4882a593Smuzhiyun sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req->shadow_reg_len);
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
481*4882a593Smuzhiyun QMI_WLFW_WLAN_CFG_REQ_V01,
482*4882a593Smuzhiyun WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN,
483*4882a593Smuzhiyun wlfw_wlan_cfg_req_msg_v01_ei, req);
484*4882a593Smuzhiyun if (ret < 0) {
485*4882a593Smuzhiyun qmi_txn_cancel(&txn);
486*4882a593Smuzhiyun ath10k_err(ar, "failed to send config request: %d\n", ret);
487*4882a593Smuzhiyun goto out;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
491*4882a593Smuzhiyun if (ret < 0)
492*4882a593Smuzhiyun goto out;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
495*4882a593Smuzhiyun ath10k_err(ar, "config request rejected: %d\n", resp.resp.error);
496*4882a593Smuzhiyun ret = -EINVAL;
497*4882a593Smuzhiyun goto out;
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi config request completed\n");
501*4882a593Smuzhiyun kfree(req);
502*4882a593Smuzhiyun return 0;
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun out:
505*4882a593Smuzhiyun kfree(req);
506*4882a593Smuzhiyun return ret;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
ath10k_qmi_wlan_enable(struct ath10k * ar,struct ath10k_qmi_wlan_enable_cfg * config,enum wlfw_driver_mode_enum_v01 mode,const char * version)509*4882a593Smuzhiyun int ath10k_qmi_wlan_enable(struct ath10k *ar,
510*4882a593Smuzhiyun struct ath10k_qmi_wlan_enable_cfg *config,
511*4882a593Smuzhiyun enum wlfw_driver_mode_enum_v01 mode,
512*4882a593Smuzhiyun const char *version)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun int ret;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi mode %d config %p\n",
517*4882a593Smuzhiyun mode, config);
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun ret = ath10k_qmi_cfg_send_sync_msg(ar, config, version);
520*4882a593Smuzhiyun if (ret) {
521*4882a593Smuzhiyun ath10k_err(ar, "failed to send qmi config: %d\n", ret);
522*4882a593Smuzhiyun return ret;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun ret = ath10k_qmi_mode_send_sync_msg(ar, mode);
526*4882a593Smuzhiyun if (ret) {
527*4882a593Smuzhiyun ath10k_err(ar, "failed to send qmi mode: %d\n", ret);
528*4882a593Smuzhiyun return ret;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun return 0;
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
ath10k_qmi_wlan_disable(struct ath10k * ar)534*4882a593Smuzhiyun int ath10k_qmi_wlan_disable(struct ath10k *ar)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01);
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi * qmi)539*4882a593Smuzhiyun static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun struct wlfw_cap_resp_msg_v01 *resp;
542*4882a593Smuzhiyun struct wlfw_cap_req_msg_v01 req = {};
543*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
544*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
545*4882a593Smuzhiyun struct qmi_txn txn;
546*4882a593Smuzhiyun int ret;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun resp = kzalloc(sizeof(*resp), GFP_KERNEL);
549*4882a593Smuzhiyun if (!resp)
550*4882a593Smuzhiyun return -ENOMEM;
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cap_resp_msg_v01_ei, resp);
553*4882a593Smuzhiyun if (ret < 0)
554*4882a593Smuzhiyun goto out;
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
557*4882a593Smuzhiyun QMI_WLFW_CAP_REQ_V01,
558*4882a593Smuzhiyun WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN,
559*4882a593Smuzhiyun wlfw_cap_req_msg_v01_ei, &req);
560*4882a593Smuzhiyun if (ret < 0) {
561*4882a593Smuzhiyun qmi_txn_cancel(&txn);
562*4882a593Smuzhiyun ath10k_err(ar, "failed to send capability request: %d\n", ret);
563*4882a593Smuzhiyun goto out;
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
567*4882a593Smuzhiyun if (ret < 0)
568*4882a593Smuzhiyun goto out;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
571*4882a593Smuzhiyun ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error);
572*4882a593Smuzhiyun ret = -EINVAL;
573*4882a593Smuzhiyun goto out;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun if (resp->chip_info_valid) {
577*4882a593Smuzhiyun qmi->chip_info.chip_id = resp->chip_info.chip_id;
578*4882a593Smuzhiyun qmi->chip_info.chip_family = resp->chip_info.chip_family;
579*4882a593Smuzhiyun } else {
580*4882a593Smuzhiyun qmi->chip_info.chip_id = 0xFF;
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun if (resp->board_info_valid)
584*4882a593Smuzhiyun qmi->board_info.board_id = resp->board_info.board_id;
585*4882a593Smuzhiyun else
586*4882a593Smuzhiyun qmi->board_info.board_id = 0xFF;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun if (resp->soc_info_valid)
589*4882a593Smuzhiyun qmi->soc_info.soc_id = resp->soc_info.soc_id;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun if (resp->fw_version_info_valid) {
592*4882a593Smuzhiyun qmi->fw_version = resp->fw_version_info.fw_version;
593*4882a593Smuzhiyun strlcpy(qmi->fw_build_timestamp, resp->fw_version_info.fw_build_timestamp,
594*4882a593Smuzhiyun sizeof(qmi->fw_build_timestamp));
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun if (resp->fw_build_id_valid)
598*4882a593Smuzhiyun strlcpy(qmi->fw_build_id, resp->fw_build_id,
599*4882a593Smuzhiyun MAX_BUILD_ID_LEN + 1);
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun if (!test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) {
602*4882a593Smuzhiyun ath10k_info(ar, "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x",
603*4882a593Smuzhiyun qmi->chip_info.chip_id, qmi->chip_info.chip_family,
604*4882a593Smuzhiyun qmi->board_info.board_id, qmi->soc_info.soc_id);
605*4882a593Smuzhiyun ath10k_info(ar, "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s",
606*4882a593Smuzhiyun qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id);
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun kfree(resp);
610*4882a593Smuzhiyun return 0;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun out:
613*4882a593Smuzhiyun kfree(resp);
614*4882a593Smuzhiyun return ret;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun
ath10k_qmi_host_cap_send_sync(struct ath10k_qmi * qmi)617*4882a593Smuzhiyun static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun struct wlfw_host_cap_resp_msg_v01 resp = {};
620*4882a593Smuzhiyun struct wlfw_host_cap_req_msg_v01 req = {};
621*4882a593Smuzhiyun struct qmi_elem_info *req_ei;
622*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
623*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
624*4882a593Smuzhiyun struct qmi_txn txn;
625*4882a593Smuzhiyun int ret;
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun req.daemon_support_valid = 1;
628*4882a593Smuzhiyun req.daemon_support = 0;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_host_cap_resp_msg_v01_ei,
631*4882a593Smuzhiyun &resp);
632*4882a593Smuzhiyun if (ret < 0)
633*4882a593Smuzhiyun goto out;
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun if (test_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags))
636*4882a593Smuzhiyun req_ei = wlfw_host_cap_8bit_req_msg_v01_ei;
637*4882a593Smuzhiyun else
638*4882a593Smuzhiyun req_ei = wlfw_host_cap_req_msg_v01_ei;
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
641*4882a593Smuzhiyun QMI_WLFW_HOST_CAP_REQ_V01,
642*4882a593Smuzhiyun WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,
643*4882a593Smuzhiyun req_ei, &req);
644*4882a593Smuzhiyun if (ret < 0) {
645*4882a593Smuzhiyun qmi_txn_cancel(&txn);
646*4882a593Smuzhiyun ath10k_err(ar, "failed to send host capability request: %d\n", ret);
647*4882a593Smuzhiyun goto out;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
651*4882a593Smuzhiyun if (ret < 0)
652*4882a593Smuzhiyun goto out;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun /* older FW didn't support this request, which is not fatal */
655*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&
656*4882a593Smuzhiyun resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) {
657*4882a593Smuzhiyun ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error);
658*4882a593Smuzhiyun ret = -EINVAL;
659*4882a593Smuzhiyun goto out;
660*4882a593Smuzhiyun }
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n");
663*4882a593Smuzhiyun return 0;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun out:
666*4882a593Smuzhiyun return ret;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun
ath10k_qmi_set_fw_log_mode(struct ath10k * ar,u8 fw_log_mode)669*4882a593Smuzhiyun int ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode)
670*4882a593Smuzhiyun {
671*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
672*4882a593Smuzhiyun struct wlfw_ini_resp_msg_v01 resp = {};
673*4882a593Smuzhiyun struct ath10k_qmi *qmi = ar_snoc->qmi;
674*4882a593Smuzhiyun struct wlfw_ini_req_msg_v01 req = {};
675*4882a593Smuzhiyun struct qmi_txn txn;
676*4882a593Smuzhiyun int ret;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun req.enablefwlog_valid = 1;
679*4882a593Smuzhiyun req.enablefwlog = fw_log_mode;
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_ini_resp_msg_v01_ei,
682*4882a593Smuzhiyun &resp);
683*4882a593Smuzhiyun if (ret < 0)
684*4882a593Smuzhiyun goto out;
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
687*4882a593Smuzhiyun QMI_WLFW_INI_REQ_V01,
688*4882a593Smuzhiyun WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,
689*4882a593Smuzhiyun wlfw_ini_req_msg_v01_ei, &req);
690*4882a593Smuzhiyun if (ret < 0) {
691*4882a593Smuzhiyun qmi_txn_cancel(&txn);
692*4882a593Smuzhiyun ath10k_err(ar, "failed to send fw log request: %d\n", ret);
693*4882a593Smuzhiyun goto out;
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
697*4882a593Smuzhiyun if (ret < 0)
698*4882a593Smuzhiyun goto out;
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
701*4882a593Smuzhiyun ath10k_err(ar, "fw log request rejected: %d\n",
702*4882a593Smuzhiyun resp.resp.error);
703*4882a593Smuzhiyun ret = -EINVAL;
704*4882a593Smuzhiyun goto out;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi fw log request completed, mode: %d\n",
707*4882a593Smuzhiyun fw_log_mode);
708*4882a593Smuzhiyun return 0;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun out:
711*4882a593Smuzhiyun return ret;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun
714*4882a593Smuzhiyun static int
ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi * qmi)715*4882a593Smuzhiyun ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun struct wlfw_ind_register_resp_msg_v01 resp = {};
718*4882a593Smuzhiyun struct wlfw_ind_register_req_msg_v01 req = {};
719*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
720*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
721*4882a593Smuzhiyun struct qmi_txn txn;
722*4882a593Smuzhiyun int ret;
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun req.client_id_valid = 1;
725*4882a593Smuzhiyun req.client_id = ATH10K_QMI_CLIENT_ID;
726*4882a593Smuzhiyun req.fw_ready_enable_valid = 1;
727*4882a593Smuzhiyun req.fw_ready_enable = 1;
728*4882a593Smuzhiyun req.msa_ready_enable_valid = 1;
729*4882a593Smuzhiyun req.msa_ready_enable = 1;
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun if (ar_snoc->xo_cal_supported) {
732*4882a593Smuzhiyun req.xo_cal_enable_valid = 1;
733*4882a593Smuzhiyun req.xo_cal_enable = 1;
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
737*4882a593Smuzhiyun wlfw_ind_register_resp_msg_v01_ei, &resp);
738*4882a593Smuzhiyun if (ret < 0)
739*4882a593Smuzhiyun goto out;
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
742*4882a593Smuzhiyun QMI_WLFW_IND_REGISTER_REQ_V01,
743*4882a593Smuzhiyun WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN,
744*4882a593Smuzhiyun wlfw_ind_register_req_msg_v01_ei, &req);
745*4882a593Smuzhiyun if (ret < 0) {
746*4882a593Smuzhiyun qmi_txn_cancel(&txn);
747*4882a593Smuzhiyun ath10k_err(ar, "failed to send indication registered request: %d\n", ret);
748*4882a593Smuzhiyun goto out;
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun
751*4882a593Smuzhiyun ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);
752*4882a593Smuzhiyun if (ret < 0)
753*4882a593Smuzhiyun goto out;
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
756*4882a593Smuzhiyun ath10k_err(ar, "indication request rejected: %d\n", resp.resp.error);
757*4882a593Smuzhiyun ret = -EINVAL;
758*4882a593Smuzhiyun goto out;
759*4882a593Smuzhiyun }
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun if (resp.fw_status_valid) {
762*4882a593Smuzhiyun if (resp.fw_status & QMI_WLFW_FW_READY_V01)
763*4882a593Smuzhiyun qmi->fw_ready = true;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi indication register request completed\n");
766*4882a593Smuzhiyun return 0;
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun out:
769*4882a593Smuzhiyun return ret;
770*4882a593Smuzhiyun }
771*4882a593Smuzhiyun
ath10k_qmi_event_server_arrive(struct ath10k_qmi * qmi)772*4882a593Smuzhiyun static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi)
773*4882a593Smuzhiyun {
774*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
775*4882a593Smuzhiyun int ret;
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun ret = ath10k_qmi_ind_register_send_sync_msg(qmi);
778*4882a593Smuzhiyun if (ret)
779*4882a593Smuzhiyun return;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun if (qmi->fw_ready) {
782*4882a593Smuzhiyun ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);
783*4882a593Smuzhiyun return;
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun ret = ath10k_qmi_host_cap_send_sync(qmi);
787*4882a593Smuzhiyun if (ret)
788*4882a593Smuzhiyun return;
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi);
791*4882a593Smuzhiyun if (ret)
792*4882a593Smuzhiyun return;
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun /*
795*4882a593Smuzhiyun * HACK: sleep for a while inbetween receiving the msa info response
796*4882a593Smuzhiyun * and the XPU update to prevent SDM845 from crashing due to a security
797*4882a593Smuzhiyun * violation, when running MPSS.AT.4.0.c2-01184-SDM845_GEN_PACK-1.
798*4882a593Smuzhiyun */
799*4882a593Smuzhiyun msleep(20);
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun ret = ath10k_qmi_setup_msa_permissions(qmi);
802*4882a593Smuzhiyun if (ret)
803*4882a593Smuzhiyun return;
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun ret = ath10k_qmi_msa_ready_send_sync_msg(qmi);
806*4882a593Smuzhiyun if (ret)
807*4882a593Smuzhiyun goto err_setup_msa;
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun ret = ath10k_qmi_cap_send_sync_msg(qmi);
810*4882a593Smuzhiyun if (ret)
811*4882a593Smuzhiyun goto err_setup_msa;
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun return;
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun err_setup_msa:
816*4882a593Smuzhiyun ath10k_qmi_remove_msa_permission(qmi);
817*4882a593Smuzhiyun }
818*4882a593Smuzhiyun
ath10k_qmi_fetch_board_file(struct ath10k_qmi * qmi)819*4882a593Smuzhiyun static int ath10k_qmi_fetch_board_file(struct ath10k_qmi *qmi)
820*4882a593Smuzhiyun {
821*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
822*4882a593Smuzhiyun int ret;
823*4882a593Smuzhiyun
824*4882a593Smuzhiyun ar->hif.bus = ATH10K_BUS_SNOC;
825*4882a593Smuzhiyun ar->id.qmi_ids_valid = true;
826*4882a593Smuzhiyun ar->id.qmi_board_id = qmi->board_info.board_id;
827*4882a593Smuzhiyun ar->id.qmi_chip_id = qmi->chip_info.chip_id;
828*4882a593Smuzhiyun ar->hw_params.fw.dir = WCN3990_HW_1_0_FW_DIR;
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun ret = ath10k_core_check_dt(ar);
831*4882a593Smuzhiyun if (ret)
832*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "DT bdf variant name not set.\n");
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun return ath10k_core_fetch_board_file(qmi->ar, ATH10K_BD_IE_BOARD);
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun static int
ath10k_qmi_driver_event_post(struct ath10k_qmi * qmi,enum ath10k_qmi_driver_event_type type,void * data)838*4882a593Smuzhiyun ath10k_qmi_driver_event_post(struct ath10k_qmi *qmi,
839*4882a593Smuzhiyun enum ath10k_qmi_driver_event_type type,
840*4882a593Smuzhiyun void *data)
841*4882a593Smuzhiyun {
842*4882a593Smuzhiyun struct ath10k_qmi_driver_event *event;
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun event = kzalloc(sizeof(*event), GFP_ATOMIC);
845*4882a593Smuzhiyun if (!event)
846*4882a593Smuzhiyun return -ENOMEM;
847*4882a593Smuzhiyun
848*4882a593Smuzhiyun event->type = type;
849*4882a593Smuzhiyun event->data = data;
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun spin_lock(&qmi->event_lock);
852*4882a593Smuzhiyun list_add_tail(&event->list, &qmi->event_list);
853*4882a593Smuzhiyun spin_unlock(&qmi->event_lock);
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun queue_work(qmi->event_wq, &qmi->event_work);
856*4882a593Smuzhiyun
857*4882a593Smuzhiyun return 0;
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun
ath10k_qmi_event_server_exit(struct ath10k_qmi * qmi)860*4882a593Smuzhiyun static void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi)
861*4882a593Smuzhiyun {
862*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
863*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun ath10k_qmi_remove_msa_permission(qmi);
866*4882a593Smuzhiyun ath10k_core_free_board_files(ar);
867*4882a593Smuzhiyun if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags))
868*4882a593Smuzhiyun ath10k_snoc_fw_crashed_dump(ar);
869*4882a593Smuzhiyun
870*4882a593Smuzhiyun ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND);
871*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service disconnected\n");
872*4882a593Smuzhiyun }
873*4882a593Smuzhiyun
ath10k_qmi_event_msa_ready(struct ath10k_qmi * qmi)874*4882a593Smuzhiyun static void ath10k_qmi_event_msa_ready(struct ath10k_qmi *qmi)
875*4882a593Smuzhiyun {
876*4882a593Smuzhiyun int ret;
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun ret = ath10k_qmi_fetch_board_file(qmi);
879*4882a593Smuzhiyun if (ret)
880*4882a593Smuzhiyun goto out;
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun ret = ath10k_qmi_bdf_dnld_send_sync(qmi);
883*4882a593Smuzhiyun if (ret)
884*4882a593Smuzhiyun goto out;
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun ret = ath10k_qmi_send_cal_report_req(qmi);
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun out:
889*4882a593Smuzhiyun return;
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun
ath10k_qmi_event_fw_ready_ind(struct ath10k_qmi * qmi)892*4882a593Smuzhiyun static int ath10k_qmi_event_fw_ready_ind(struct ath10k_qmi *qmi)
893*4882a593Smuzhiyun {
894*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw ready event received\n");
897*4882a593Smuzhiyun ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun return 0;
900*4882a593Smuzhiyun }
901*4882a593Smuzhiyun
ath10k_qmi_fw_ready_ind(struct qmi_handle * qmi_hdl,struct sockaddr_qrtr * sq,struct qmi_txn * txn,const void * data)902*4882a593Smuzhiyun static void ath10k_qmi_fw_ready_ind(struct qmi_handle *qmi_hdl,
903*4882a593Smuzhiyun struct sockaddr_qrtr *sq,
904*4882a593Smuzhiyun struct qmi_txn *txn, const void *data)
905*4882a593Smuzhiyun {
906*4882a593Smuzhiyun struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_FW_READY_IND, NULL);
909*4882a593Smuzhiyun }
910*4882a593Smuzhiyun
ath10k_qmi_msa_ready_ind(struct qmi_handle * qmi_hdl,struct sockaddr_qrtr * sq,struct qmi_txn * txn,const void * data)911*4882a593Smuzhiyun static void ath10k_qmi_msa_ready_ind(struct qmi_handle *qmi_hdl,
912*4882a593Smuzhiyun struct sockaddr_qrtr *sq,
913*4882a593Smuzhiyun struct qmi_txn *txn, const void *data)
914*4882a593Smuzhiyun {
915*4882a593Smuzhiyun struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
916*4882a593Smuzhiyun
917*4882a593Smuzhiyun ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_MSA_READY_IND, NULL);
918*4882a593Smuzhiyun }
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun static struct qmi_msg_handler qmi_msg_handler[] = {
921*4882a593Smuzhiyun {
922*4882a593Smuzhiyun .type = QMI_INDICATION,
923*4882a593Smuzhiyun .msg_id = QMI_WLFW_FW_READY_IND_V01,
924*4882a593Smuzhiyun .ei = wlfw_fw_ready_ind_msg_v01_ei,
925*4882a593Smuzhiyun .decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01),
926*4882a593Smuzhiyun .fn = ath10k_qmi_fw_ready_ind,
927*4882a593Smuzhiyun },
928*4882a593Smuzhiyun {
929*4882a593Smuzhiyun .type = QMI_INDICATION,
930*4882a593Smuzhiyun .msg_id = QMI_WLFW_MSA_READY_IND_V01,
931*4882a593Smuzhiyun .ei = wlfw_msa_ready_ind_msg_v01_ei,
932*4882a593Smuzhiyun .decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01),
933*4882a593Smuzhiyun .fn = ath10k_qmi_msa_ready_ind,
934*4882a593Smuzhiyun },
935*4882a593Smuzhiyun {}
936*4882a593Smuzhiyun };
937*4882a593Smuzhiyun
ath10k_qmi_new_server(struct qmi_handle * qmi_hdl,struct qmi_service * service)938*4882a593Smuzhiyun static int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,
939*4882a593Smuzhiyun struct qmi_service *service)
940*4882a593Smuzhiyun {
941*4882a593Smuzhiyun struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
942*4882a593Smuzhiyun struct sockaddr_qrtr *sq = &qmi->sq;
943*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
944*4882a593Smuzhiyun int ret;
945*4882a593Smuzhiyun
946*4882a593Smuzhiyun sq->sq_family = AF_QIPCRTR;
947*4882a593Smuzhiyun sq->sq_node = service->node;
948*4882a593Smuzhiyun sq->sq_port = service->port;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq,
953*4882a593Smuzhiyun sizeof(qmi->sq), 0);
954*4882a593Smuzhiyun if (ret) {
955*4882a593Smuzhiyun ath10k_err(ar, "failed to connect to a remote QMI service port\n");
956*4882a593Smuzhiyun return ret;
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun
959*4882a593Smuzhiyun ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wifi fw qmi service connected\n");
960*4882a593Smuzhiyun ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_ARRIVE, NULL);
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun return ret;
963*4882a593Smuzhiyun }
964*4882a593Smuzhiyun
ath10k_qmi_del_server(struct qmi_handle * qmi_hdl,struct qmi_service * service)965*4882a593Smuzhiyun static void ath10k_qmi_del_server(struct qmi_handle *qmi_hdl,
966*4882a593Smuzhiyun struct qmi_service *service)
967*4882a593Smuzhiyun {
968*4882a593Smuzhiyun struct ath10k_qmi *qmi =
969*4882a593Smuzhiyun container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun qmi->fw_ready = false;
972*4882a593Smuzhiyun
973*4882a593Smuzhiyun /*
974*4882a593Smuzhiyun * The del_server event is to be processed only if coming from
975*4882a593Smuzhiyun * the qmi server. The qmi infrastructure sends del_server, when
976*4882a593Smuzhiyun * any client releases the qmi handle. In this case do not process
977*4882a593Smuzhiyun * this del_server event.
978*4882a593Smuzhiyun */
979*4882a593Smuzhiyun if (qmi->state == ATH10K_QMI_STATE_INIT_DONE)
980*4882a593Smuzhiyun ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_EXIT,
981*4882a593Smuzhiyun NULL);
982*4882a593Smuzhiyun }
983*4882a593Smuzhiyun
984*4882a593Smuzhiyun static struct qmi_ops ath10k_qmi_ops = {
985*4882a593Smuzhiyun .new_server = ath10k_qmi_new_server,
986*4882a593Smuzhiyun .del_server = ath10k_qmi_del_server,
987*4882a593Smuzhiyun };
988*4882a593Smuzhiyun
ath10k_qmi_driver_event_work(struct work_struct * work)989*4882a593Smuzhiyun static void ath10k_qmi_driver_event_work(struct work_struct *work)
990*4882a593Smuzhiyun {
991*4882a593Smuzhiyun struct ath10k_qmi *qmi = container_of(work, struct ath10k_qmi,
992*4882a593Smuzhiyun event_work);
993*4882a593Smuzhiyun struct ath10k_qmi_driver_event *event;
994*4882a593Smuzhiyun struct ath10k *ar = qmi->ar;
995*4882a593Smuzhiyun
996*4882a593Smuzhiyun spin_lock(&qmi->event_lock);
997*4882a593Smuzhiyun while (!list_empty(&qmi->event_list)) {
998*4882a593Smuzhiyun event = list_first_entry(&qmi->event_list,
999*4882a593Smuzhiyun struct ath10k_qmi_driver_event, list);
1000*4882a593Smuzhiyun list_del(&event->list);
1001*4882a593Smuzhiyun spin_unlock(&qmi->event_lock);
1002*4882a593Smuzhiyun
1003*4882a593Smuzhiyun switch (event->type) {
1004*4882a593Smuzhiyun case ATH10K_QMI_EVENT_SERVER_ARRIVE:
1005*4882a593Smuzhiyun ath10k_qmi_event_server_arrive(qmi);
1006*4882a593Smuzhiyun break;
1007*4882a593Smuzhiyun case ATH10K_QMI_EVENT_SERVER_EXIT:
1008*4882a593Smuzhiyun ath10k_qmi_event_server_exit(qmi);
1009*4882a593Smuzhiyun break;
1010*4882a593Smuzhiyun case ATH10K_QMI_EVENT_FW_READY_IND:
1011*4882a593Smuzhiyun ath10k_qmi_event_fw_ready_ind(qmi);
1012*4882a593Smuzhiyun break;
1013*4882a593Smuzhiyun case ATH10K_QMI_EVENT_MSA_READY_IND:
1014*4882a593Smuzhiyun ath10k_qmi_event_msa_ready(qmi);
1015*4882a593Smuzhiyun break;
1016*4882a593Smuzhiyun default:
1017*4882a593Smuzhiyun ath10k_warn(ar, "invalid event type: %d", event->type);
1018*4882a593Smuzhiyun break;
1019*4882a593Smuzhiyun }
1020*4882a593Smuzhiyun kfree(event);
1021*4882a593Smuzhiyun spin_lock(&qmi->event_lock);
1022*4882a593Smuzhiyun }
1023*4882a593Smuzhiyun spin_unlock(&qmi->event_lock);
1024*4882a593Smuzhiyun }
1025*4882a593Smuzhiyun
ath10k_qmi_init(struct ath10k * ar,u32 msa_size)1026*4882a593Smuzhiyun int ath10k_qmi_init(struct ath10k *ar, u32 msa_size)
1027*4882a593Smuzhiyun {
1028*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
1029*4882a593Smuzhiyun struct device *dev = ar->dev;
1030*4882a593Smuzhiyun struct ath10k_qmi *qmi;
1031*4882a593Smuzhiyun int ret;
1032*4882a593Smuzhiyun
1033*4882a593Smuzhiyun qmi = kzalloc(sizeof(*qmi), GFP_KERNEL);
1034*4882a593Smuzhiyun if (!qmi)
1035*4882a593Smuzhiyun return -ENOMEM;
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyun qmi->ar = ar;
1038*4882a593Smuzhiyun ar_snoc->qmi = qmi;
1039*4882a593Smuzhiyun
1040*4882a593Smuzhiyun if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm"))
1041*4882a593Smuzhiyun qmi->msa_fixed_perm = true;
1042*4882a593Smuzhiyun
1043*4882a593Smuzhiyun ret = qmi_handle_init(&qmi->qmi_hdl,
1044*4882a593Smuzhiyun WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
1045*4882a593Smuzhiyun &ath10k_qmi_ops, qmi_msg_handler);
1046*4882a593Smuzhiyun if (ret)
1047*4882a593Smuzhiyun goto err;
1048*4882a593Smuzhiyun
1049*4882a593Smuzhiyun qmi->event_wq = alloc_workqueue("ath10k_qmi_driver_event",
1050*4882a593Smuzhiyun WQ_UNBOUND, 1);
1051*4882a593Smuzhiyun if (!qmi->event_wq) {
1052*4882a593Smuzhiyun ath10k_err(ar, "failed to allocate workqueue\n");
1053*4882a593Smuzhiyun ret = -EFAULT;
1054*4882a593Smuzhiyun goto err_release_qmi_handle;
1055*4882a593Smuzhiyun }
1056*4882a593Smuzhiyun
1057*4882a593Smuzhiyun INIT_LIST_HEAD(&qmi->event_list);
1058*4882a593Smuzhiyun spin_lock_init(&qmi->event_lock);
1059*4882a593Smuzhiyun INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work);
1060*4882a593Smuzhiyun
1061*4882a593Smuzhiyun ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01,
1062*4882a593Smuzhiyun WLFW_SERVICE_VERS_V01, 0);
1063*4882a593Smuzhiyun if (ret)
1064*4882a593Smuzhiyun goto err_qmi_lookup;
1065*4882a593Smuzhiyun
1066*4882a593Smuzhiyun qmi->state = ATH10K_QMI_STATE_INIT_DONE;
1067*4882a593Smuzhiyun return 0;
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun err_qmi_lookup:
1070*4882a593Smuzhiyun destroy_workqueue(qmi->event_wq);
1071*4882a593Smuzhiyun
1072*4882a593Smuzhiyun err_release_qmi_handle:
1073*4882a593Smuzhiyun qmi_handle_release(&qmi->qmi_hdl);
1074*4882a593Smuzhiyun
1075*4882a593Smuzhiyun err:
1076*4882a593Smuzhiyun kfree(qmi);
1077*4882a593Smuzhiyun return ret;
1078*4882a593Smuzhiyun }
1079*4882a593Smuzhiyun
ath10k_qmi_deinit(struct ath10k * ar)1080*4882a593Smuzhiyun int ath10k_qmi_deinit(struct ath10k *ar)
1081*4882a593Smuzhiyun {
1082*4882a593Smuzhiyun struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
1083*4882a593Smuzhiyun struct ath10k_qmi *qmi = ar_snoc->qmi;
1084*4882a593Smuzhiyun
1085*4882a593Smuzhiyun qmi->state = ATH10K_QMI_STATE_DEINIT;
1086*4882a593Smuzhiyun qmi_handle_release(&qmi->qmi_hdl);
1087*4882a593Smuzhiyun cancel_work_sync(&qmi->event_work);
1088*4882a593Smuzhiyun destroy_workqueue(qmi->event_wq);
1089*4882a593Smuzhiyun kfree(qmi);
1090*4882a593Smuzhiyun ar_snoc->qmi = NULL;
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun return 0;
1093*4882a593Smuzhiyun }
1094