Path: blob/main/sys/contrib/dev/athk/ath10k/qmi.c
107442 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2018 The Linux Foundation. All rights reserved.3* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.4*/56#include <linux/completion.h>7#include <linux/device.h>8#include <linux/debugfs.h>9#include <linux/idr.h>10#include <linux/kernel.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/module.h>14#include <linux/net.h>15#include <linux/platform_device.h>16#include <linux/firmware/qcom/qcom_scm.h>17#include <linux/soc/qcom/smem.h>18#include <linux/string.h>19#include <net/sock.h>2021#include "debug.h"22#include "snoc.h"2324#define ATH10K_QMI_CLIENT_ID 0x4b4e454c25#define ATH10K_QMI_TIMEOUT 3026#define SMEM_IMAGE_VERSION_TABLE 46927#define SMEM_IMAGE_TABLE_CNSS_INDEX 1328#define SMEM_IMAGE_VERSION_ENTRY_SIZE 12829#define SMEM_IMAGE_VERSION_NAME_SIZE 753031static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,32struct ath10k_msa_mem_info *mem_info)33{34struct qcom_scm_vmperm dst_perms[3];35struct ath10k *ar = qmi->ar;36u64 src_perms;37u32 perm_count;38int ret;3940src_perms = BIT(QCOM_SCM_VMID_HLOS);4142dst_perms[0].vmid = QCOM_SCM_VMID_MSS_MSA;43dst_perms[0].perm = QCOM_SCM_PERM_RW;44dst_perms[1].vmid = QCOM_SCM_VMID_WLAN;45dst_perms[1].perm = QCOM_SCM_PERM_RW;4647if (mem_info->secure) {48perm_count = 2;49} else {50dst_perms[2].vmid = QCOM_SCM_VMID_WLAN_CE;51dst_perms[2].perm = QCOM_SCM_PERM_RW;52perm_count = 3;53}5455ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,56&src_perms, dst_perms, perm_count);57if (ret < 0)58ath10k_err(ar, "failed to assign msa map permissions: %d\n", ret);5960return ret;61}6263static int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi,64struct ath10k_msa_mem_info *mem_info)65{66struct qcom_scm_vmperm dst_perms;67struct ath10k *ar = qmi->ar;68u64 src_perms;69int ret;7071src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN);7273if (!mem_info->secure)74src_perms |= BIT(QCOM_SCM_VMID_WLAN_CE);7576dst_perms.vmid = QCOM_SCM_VMID_HLOS;77dst_perms.perm = QCOM_SCM_PERM_RW;7879ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,80&src_perms, &dst_perms, 1);81if (ret < 0)82ath10k_err(ar, "failed to unmap msa permissions: %d\n", ret);8384return ret;85}8687static int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi)88{89int ret;90int i;9192if (qmi->msa_fixed_perm)93return 0;9495for (i = 0; i < qmi->nr_mem_region; i++) {96ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]);97if (ret)98goto err_unmap;99}100101return 0;102103err_unmap:104for (i--; i >= 0; i--)105ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);106return ret;107}108109static void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi)110{111int i;112113if (qmi->msa_fixed_perm)114return;115116for (i = 0; i < qmi->nr_mem_region; i++)117ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);118}119120static int ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi *qmi)121{122struct wlfw_msa_info_resp_msg_v01 resp = {};123struct wlfw_msa_info_req_msg_v01 req = {};124struct ath10k *ar = qmi->ar;125phys_addr_t max_mapped_addr;126struct qmi_txn txn;127int ret;128int i;129130req.msa_addr = ar->msa.paddr;131req.size = ar->msa.mem_size;132133ret = qmi_txn_init(&qmi->qmi_hdl, &txn,134wlfw_msa_info_resp_msg_v01_ei, &resp);135if (ret < 0)136goto out;137138ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,139QMI_WLFW_MSA_INFO_REQ_V01,140WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,141wlfw_msa_info_req_msg_v01_ei, &req);142if (ret < 0) {143qmi_txn_cancel(&txn);144ath10k_err(ar, "failed to send msa mem info req: %d\n", ret);145goto out;146}147148ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);149if (ret < 0)150goto out;151152if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {153ath10k_err(ar, "msa info req rejected: %d\n", resp.resp.error);154ret = -EINVAL;155goto out;156}157158if (resp.mem_region_info_len > QMI_WLFW_MAX_MEM_REG_V01) {159ath10k_err(ar, "invalid memory region length received: %d\n",160resp.mem_region_info_len);161ret = -EINVAL;162goto out;163}164165max_mapped_addr = ar->msa.paddr + ar->msa.mem_size;166qmi->nr_mem_region = resp.mem_region_info_len;167for (i = 0; i < resp.mem_region_info_len; i++) {168if (resp.mem_region_info[i].size > ar->msa.mem_size ||169resp.mem_region_info[i].region_addr > max_mapped_addr ||170resp.mem_region_info[i].region_addr < ar->msa.paddr ||171resp.mem_region_info[i].size +172resp.mem_region_info[i].region_addr > max_mapped_addr) {173ath10k_err(ar, "received out of range memory region address 0x%llx with size 0x%x, aborting\n",174resp.mem_region_info[i].region_addr,175resp.mem_region_info[i].size);176ret = -EINVAL;177goto fail_unwind;178}179qmi->mem_region[i].addr = resp.mem_region_info[i].region_addr;180qmi->mem_region[i].size = resp.mem_region_info[i].size;181qmi->mem_region[i].secure = resp.mem_region_info[i].secure_flag;182ath10k_dbg(ar, ATH10K_DBG_QMI,183"qmi msa mem region %d addr 0x%pa size 0x%x flag 0x%08x\n",184i, &qmi->mem_region[i].addr,185qmi->mem_region[i].size,186qmi->mem_region[i].secure);187}188189ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem info request completed\n");190return 0;191192fail_unwind:193memset(&qmi->mem_region[0], 0, sizeof(qmi->mem_region[0]) * i);194out:195return ret;196}197198static int ath10k_qmi_msa_ready_send_sync_msg(struct ath10k_qmi *qmi)199{200struct wlfw_msa_ready_resp_msg_v01 resp = {};201struct wlfw_msa_ready_req_msg_v01 req = {};202struct ath10k *ar = qmi->ar;203struct qmi_txn txn;204int ret;205206ret = qmi_txn_init(&qmi->qmi_hdl, &txn,207wlfw_msa_ready_resp_msg_v01_ei, &resp);208if (ret < 0)209goto out;210211ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,212QMI_WLFW_MSA_READY_REQ_V01,213WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN,214wlfw_msa_ready_req_msg_v01_ei, &req);215if (ret < 0) {216qmi_txn_cancel(&txn);217ath10k_err(ar, "failed to send msa mem ready request: %d\n", ret);218goto out;219}220221ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);222if (ret < 0)223goto out;224225if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {226ath10k_err(ar, "msa ready request rejected: %d\n", resp.resp.error);227ret = -EINVAL;228}229230ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa mem ready request completed\n");231return 0;232233out:234return ret;235}236237static int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi)238{239struct wlfw_bdf_download_resp_msg_v01 resp = {};240struct wlfw_bdf_download_req_msg_v01 *req;241struct ath10k *ar = qmi->ar;242unsigned int remaining;243struct qmi_txn txn;244const u8 *temp;245int ret;246247req = kzalloc(sizeof(*req), GFP_KERNEL);248if (!req)249return -ENOMEM;250251temp = ar->normal_mode_fw.board_data;252remaining = ar->normal_mode_fw.board_len;253254while (remaining) {255req->valid = 1;256req->file_id_valid = 1;257req->file_id = 0;258req->total_size_valid = 1;259req->total_size = ar->normal_mode_fw.board_len;260req->seg_id_valid = 1;261req->data_valid = 1;262req->end_valid = 1;263264if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {265req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;266} else {267req->data_len = remaining;268req->end = 1;269}270271memcpy(req->data, temp, req->data_len);272273ret = qmi_txn_init(&qmi->qmi_hdl, &txn,274wlfw_bdf_download_resp_msg_v01_ei,275&resp);276if (ret < 0)277goto out;278279ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,280QMI_WLFW_BDF_DOWNLOAD_REQ_V01,281WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,282wlfw_bdf_download_req_msg_v01_ei, req);283if (ret < 0) {284qmi_txn_cancel(&txn);285goto out;286}287288ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);289290if (ret < 0)291goto out;292293/* end = 1 triggers a CRC check on the BDF. If this fails, we294* get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still295* willing to use the BDF. For some platforms, all the valid296* released BDFs fail this CRC check, so attempt to detect this297* scenario and treat it as non-fatal.298*/299if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&300!(req->end == 1 &&301resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) {302ath10k_err(ar, "failed to download board data file: %d\n",303resp.resp.error);304ret = -EINVAL;305goto out;306}307308remaining -= req->data_len;309temp += req->data_len;310req->seg_id++;311}312313ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi bdf download request completed\n");314315kfree(req);316return 0;317318out:319kfree(req);320return ret;321}322323static int ath10k_qmi_send_cal_report_req(struct ath10k_qmi *qmi)324{325struct wlfw_cal_report_resp_msg_v01 resp = {};326struct wlfw_cal_report_req_msg_v01 req = {};327struct ath10k *ar = qmi->ar;328struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);329struct qmi_txn txn;330int i, j = 0;331int ret;332333if (ar_snoc->xo_cal_supported) {334req.xo_cal_data_valid = 1;335req.xo_cal_data = ar_snoc->xo_cal_data;336}337338ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cal_report_resp_msg_v01_ei,339&resp);340if (ret < 0)341goto out;342343for (i = 0; i < QMI_WLFW_MAX_NUM_CAL_V01; i++) {344if (qmi->cal_data[i].total_size &&345qmi->cal_data[i].data) {346req.meta_data[j] = qmi->cal_data[i].cal_id;347j++;348}349}350req.meta_data_len = j;351352ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,353QMI_WLFW_CAL_REPORT_REQ_V01,354WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN,355wlfw_cal_report_req_msg_v01_ei, &req);356if (ret < 0) {357qmi_txn_cancel(&txn);358ath10k_err(ar, "failed to send calibration request: %d\n", ret);359goto out;360}361362ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);363if (ret < 0)364goto out;365366if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {367ath10k_err(ar, "calibration request rejected: %d\n", resp.resp.error);368ret = -EINVAL;369goto out;370}371372ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi cal report request completed\n");373return 0;374375out:376return ret;377}378379static int380ath10k_qmi_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode)381{382struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);383struct ath10k_qmi *qmi = ar_snoc->qmi;384struct wlfw_wlan_mode_resp_msg_v01 resp = {};385struct wlfw_wlan_mode_req_msg_v01 req = {};386struct qmi_txn txn;387int ret;388389ret = qmi_txn_init(&qmi->qmi_hdl, &txn,390wlfw_wlan_mode_resp_msg_v01_ei,391&resp);392if (ret < 0)393goto out;394395req.mode = mode;396req.hw_debug_valid = 1;397req.hw_debug = 0;398399ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,400QMI_WLFW_WLAN_MODE_REQ_V01,401WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN,402wlfw_wlan_mode_req_msg_v01_ei, &req);403if (ret < 0) {404qmi_txn_cancel(&txn);405ath10k_err(ar, "failed to send wlan mode %d request: %d\n", mode, ret);406goto out;407}408409ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);410if (ret < 0)411goto out;412413if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {414ath10k_err(ar, "more request rejected: %d\n", resp.resp.error);415ret = -EINVAL;416goto out;417}418419ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wlan mode req completed: %d\n", mode);420return 0;421422out:423return ret;424}425426static int427ath10k_qmi_cfg_send_sync_msg(struct ath10k *ar,428struct ath10k_qmi_wlan_enable_cfg *config,429const char *version)430{431struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);432struct ath10k_qmi *qmi = ar_snoc->qmi;433struct wlfw_wlan_cfg_resp_msg_v01 resp = {};434struct wlfw_wlan_cfg_req_msg_v01 *req;435struct qmi_txn txn;436int ret;437u32 i;438439req = kzalloc(sizeof(*req), GFP_KERNEL);440if (!req)441return -ENOMEM;442443ret = qmi_txn_init(&qmi->qmi_hdl, &txn,444wlfw_wlan_cfg_resp_msg_v01_ei,445&resp);446if (ret < 0)447goto out;448449req->host_version_valid = 0;450451req->tgt_cfg_valid = 1;452if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)453req->tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;454else455req->tgt_cfg_len = config->num_ce_tgt_cfg;456for (i = 0; i < req->tgt_cfg_len; i++) {457req->tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;458req->tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;459req->tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;460req->tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;461req->tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;462}463464req->svc_cfg_valid = 1;465if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)466req->svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;467else468req->svc_cfg_len = config->num_ce_svc_pipe_cfg;469for (i = 0; i < req->svc_cfg_len; i++) {470req->svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;471req->svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;472req->svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;473}474475req->shadow_reg_valid = 1;476if (config->num_shadow_reg_cfg >477QMI_WLFW_MAX_NUM_SHADOW_REG_V01)478req->shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;479else480req->shadow_reg_len = config->num_shadow_reg_cfg;481482memcpy(req->shadow_reg, config->shadow_reg_cfg,483sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req->shadow_reg_len);484485ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,486QMI_WLFW_WLAN_CFG_REQ_V01,487WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN,488wlfw_wlan_cfg_req_msg_v01_ei, req);489if (ret < 0) {490qmi_txn_cancel(&txn);491ath10k_err(ar, "failed to send config request: %d\n", ret);492goto out;493}494495ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);496if (ret < 0)497goto out;498499if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {500ath10k_err(ar, "config request rejected: %d\n", resp.resp.error);501ret = -EINVAL;502goto out;503}504505ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi config request completed\n");506kfree(req);507return 0;508509out:510kfree(req);511return ret;512}513514int ath10k_qmi_wlan_enable(struct ath10k *ar,515struct ath10k_qmi_wlan_enable_cfg *config,516enum wlfw_driver_mode_enum_v01 mode,517const char *version)518{519int ret;520521ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi mode %d config %p\n",522mode, config);523524ret = ath10k_qmi_cfg_send_sync_msg(ar, config, version);525if (ret) {526ath10k_err(ar, "failed to send qmi config: %d\n", ret);527return ret;528}529530ret = ath10k_qmi_mode_send_sync_msg(ar, mode);531if (ret) {532ath10k_err(ar, "failed to send qmi mode: %d\n", ret);533return ret;534}535536return 0;537}538539int ath10k_qmi_wlan_disable(struct ath10k *ar)540{541return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01);542}543544static void ath10k_qmi_add_wlan_ver_smem(struct ath10k *ar, const char *fw_build_id)545{546u8 *table_ptr;547size_t smem_item_size;548const u32 smem_img_idx_wlan = SMEM_IMAGE_TABLE_CNSS_INDEX *549SMEM_IMAGE_VERSION_ENTRY_SIZE;550551table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY,552SMEM_IMAGE_VERSION_TABLE,553&smem_item_size);554555if (IS_ERR(table_ptr)) {556ath10k_err(ar, "smem image version table not found\n");557return;558}559560if (smem_img_idx_wlan + SMEM_IMAGE_VERSION_ENTRY_SIZE >561smem_item_size) {562ath10k_err(ar, "smem block size too small: %zu\n",563smem_item_size);564return;565}566567strscpy(table_ptr + smem_img_idx_wlan, fw_build_id,568SMEM_IMAGE_VERSION_NAME_SIZE);569}570571static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)572{573struct wlfw_cap_resp_msg_v01 *resp;574struct wlfw_cap_req_msg_v01 req = {};575struct ath10k *ar = qmi->ar;576struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);577struct qmi_txn txn;578int ret;579580resp = kzalloc(sizeof(*resp), GFP_KERNEL);581if (!resp)582return -ENOMEM;583584ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cap_resp_msg_v01_ei, resp);585if (ret < 0)586goto out;587588ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,589QMI_WLFW_CAP_REQ_V01,590WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN,591wlfw_cap_req_msg_v01_ei, &req);592if (ret < 0) {593qmi_txn_cancel(&txn);594ath10k_err(ar, "failed to send capability request: %d\n", ret);595goto out;596}597598ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);599if (ret < 0)600goto out;601602if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {603ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error);604ret = -EINVAL;605goto out;606}607608if (resp->chip_info_valid) {609qmi->chip_info.chip_id = resp->chip_info.chip_id;610qmi->chip_info.chip_family = resp->chip_info.chip_family;611} else {612qmi->chip_info.chip_id = 0xFF;613}614615if (resp->board_info_valid)616qmi->board_info.board_id = resp->board_info.board_id;617else618qmi->board_info.board_id = 0xFF;619620if (resp->soc_info_valid)621qmi->soc_info.soc_id = resp->soc_info.soc_id;622623if (resp->fw_version_info_valid) {624qmi->fw_version = resp->fw_version_info.fw_version;625strscpy(qmi->fw_build_timestamp, resp->fw_version_info.fw_build_timestamp,626sizeof(qmi->fw_build_timestamp));627}628629if (resp->fw_build_id_valid)630strscpy(qmi->fw_build_id, resp->fw_build_id,631MAX_BUILD_ID_LEN + 1);632633if (!test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) {634ath10k_info(ar, "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x",635qmi->chip_info.chip_id, qmi->chip_info.chip_family,636qmi->board_info.board_id, qmi->soc_info.soc_id);637ath10k_info(ar, "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s",638qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id);639}640641if (resp->fw_build_id_valid)642ath10k_qmi_add_wlan_ver_smem(ar, qmi->fw_build_id);643644kfree(resp);645return 0;646647out:648kfree(resp);649return ret;650}651652static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)653{654struct wlfw_host_cap_resp_msg_v01 resp = {};655struct wlfw_host_cap_req_msg_v01 req = {};656const struct qmi_elem_info *req_ei;657struct ath10k *ar = qmi->ar;658struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);659struct qmi_txn txn;660int ret;661662req.daemon_support_valid = 1;663req.daemon_support = 0;664665ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_host_cap_resp_msg_v01_ei,666&resp);667if (ret < 0)668goto out;669670if (test_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags))671req_ei = wlfw_host_cap_8bit_req_msg_v01_ei;672else673req_ei = wlfw_host_cap_req_msg_v01_ei;674675ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,676QMI_WLFW_HOST_CAP_REQ_V01,677WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,678req_ei, &req);679if (ret < 0) {680qmi_txn_cancel(&txn);681ath10k_err(ar, "failed to send host capability request: %d\n", ret);682goto out;683}684685ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);686if (ret < 0)687goto out;688689/* older FW didn't support this request, which is not fatal */690if (resp.resp.result != QMI_RESULT_SUCCESS_V01 &&691resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) {692ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error);693ret = -EINVAL;694goto out;695}696697ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n");698return 0;699700out:701return ret;702}703704int ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode)705{706struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);707struct wlfw_ini_resp_msg_v01 resp = {};708struct ath10k_qmi *qmi = ar_snoc->qmi;709struct wlfw_ini_req_msg_v01 req = {};710struct qmi_txn txn;711int ret;712713req.enablefwlog_valid = 1;714req.enablefwlog = fw_log_mode;715716ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_ini_resp_msg_v01_ei,717&resp);718if (ret < 0)719goto out;720721ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,722QMI_WLFW_INI_REQ_V01,723WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,724wlfw_ini_req_msg_v01_ei, &req);725if (ret < 0) {726qmi_txn_cancel(&txn);727ath10k_err(ar, "failed to send fw log request: %d\n", ret);728goto out;729}730731ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);732if (ret < 0)733goto out;734735if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {736ath10k_err(ar, "fw log request rejected: %d\n",737resp.resp.error);738ret = -EINVAL;739goto out;740}741ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi fw log request completed, mode: %d\n",742fw_log_mode);743return 0;744745out:746return ret;747}748749static int750ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)751{752struct wlfw_ind_register_resp_msg_v01 resp = {};753struct wlfw_ind_register_req_msg_v01 req = {};754struct ath10k *ar = qmi->ar;755struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);756struct qmi_txn txn;757int ret;758759req.client_id_valid = 1;760req.client_id = ATH10K_QMI_CLIENT_ID;761req.fw_ready_enable_valid = 1;762req.fw_ready_enable = 1;763req.msa_ready_enable_valid = 1;764req.msa_ready_enable = 1;765766if (ar_snoc->xo_cal_supported) {767req.xo_cal_enable_valid = 1;768req.xo_cal_enable = 1;769}770771ret = qmi_txn_init(&qmi->qmi_hdl, &txn,772wlfw_ind_register_resp_msg_v01_ei, &resp);773if (ret < 0)774goto out;775776ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,777QMI_WLFW_IND_REGISTER_REQ_V01,778WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN,779wlfw_ind_register_req_msg_v01_ei, &req);780if (ret < 0) {781qmi_txn_cancel(&txn);782ath10k_err(ar, "failed to send indication registered request: %d\n", ret);783goto out;784}785786ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ);787if (ret < 0)788goto out;789790if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {791ath10k_err(ar, "indication request rejected: %d\n", resp.resp.error);792ret = -EINVAL;793goto out;794}795796if (resp.fw_status_valid) {797if (resp.fw_status & QMI_WLFW_FW_READY_V01)798qmi->fw_ready = true;799}800ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi indication register request completed\n");801return 0;802803out:804return ret;805}806807static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi)808{809struct ath10k *ar = qmi->ar;810int ret;811812ret = ath10k_qmi_ind_register_send_sync_msg(qmi);813if (ret)814return;815816if (qmi->fw_ready) {817ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);818return;819}820821ret = ath10k_qmi_host_cap_send_sync(qmi);822if (ret)823return;824825ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi);826if (ret)827return;828829/*830* HACK: sleep for a while between receiving the msa info response831* and the XPU update to prevent SDM845 from crashing due to a security832* violation, when running MPSS.AT.4.0.c2-01184-SDM845_GEN_PACK-1.833*/834msleep(20);835836ret = ath10k_qmi_setup_msa_permissions(qmi);837if (ret)838return;839840ret = ath10k_qmi_msa_ready_send_sync_msg(qmi);841if (ret)842goto err_setup_msa;843844ret = ath10k_qmi_cap_send_sync_msg(qmi);845if (ret)846goto err_setup_msa;847848return;849850err_setup_msa:851ath10k_qmi_remove_msa_permission(qmi);852}853854static int ath10k_qmi_fetch_board_file(struct ath10k_qmi *qmi)855{856struct ath10k *ar = qmi->ar;857int ret;858859ar->hif.bus = ATH10K_BUS_SNOC;860ar->id.qmi_ids_valid = true;861ar->id.qmi_board_id = qmi->board_info.board_id;862ar->id.qmi_chip_id = qmi->chip_info.chip_id;863ar->hw_params.fw.dir = WCN3990_HW_1_0_FW_DIR;864865ret = ath10k_core_check_dt(ar);866if (ret)867ath10k_dbg(ar, ATH10K_DBG_QMI, "DT bdf variant name not set.\n");868869return ath10k_core_fetch_board_file(qmi->ar, ATH10K_BD_IE_BOARD);870}871872static int873ath10k_qmi_driver_event_post(struct ath10k_qmi *qmi,874enum ath10k_qmi_driver_event_type type,875void *data)876{877struct ath10k_qmi_driver_event *event;878879event = kzalloc(sizeof(*event), GFP_ATOMIC);880if (!event)881return -ENOMEM;882883event->type = type;884event->data = data;885886spin_lock(&qmi->event_lock);887list_add_tail(&event->list, &qmi->event_list);888spin_unlock(&qmi->event_lock);889890queue_work(qmi->event_wq, &qmi->event_work);891892return 0;893}894895static void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi)896{897struct ath10k *ar = qmi->ar;898struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);899900ath10k_qmi_remove_msa_permission(qmi);901ath10k_core_free_board_files(ar);902if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags) &&903!test_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags))904ath10k_snoc_fw_crashed_dump(ar);905906ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND);907ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service disconnected\n");908}909910static void ath10k_qmi_event_msa_ready(struct ath10k_qmi *qmi)911{912int ret;913914ret = ath10k_qmi_fetch_board_file(qmi);915if (ret)916goto out;917918ret = ath10k_qmi_bdf_dnld_send_sync(qmi);919if (ret)920goto out;921922ret = ath10k_qmi_send_cal_report_req(qmi);923924out:925return;926}927928static int ath10k_qmi_event_fw_ready_ind(struct ath10k_qmi *qmi)929{930struct ath10k *ar = qmi->ar;931932ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw ready event received\n");933ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_READY_IND);934935return 0;936}937938static void ath10k_qmi_fw_ready_ind(struct qmi_handle *qmi_hdl,939struct sockaddr_qrtr *sq,940struct qmi_txn *txn, const void *data)941{942struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);943944ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_FW_READY_IND, NULL);945}946947static void ath10k_qmi_msa_ready_ind(struct qmi_handle *qmi_hdl,948struct sockaddr_qrtr *sq,949struct qmi_txn *txn, const void *data)950{951struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);952953ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_MSA_READY_IND, NULL);954}955956static const struct qmi_msg_handler qmi_msg_handler[] = {957{958.type = QMI_INDICATION,959.msg_id = QMI_WLFW_FW_READY_IND_V01,960.ei = wlfw_fw_ready_ind_msg_v01_ei,961.decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01),962.fn = ath10k_qmi_fw_ready_ind,963},964{965.type = QMI_INDICATION,966.msg_id = QMI_WLFW_MSA_READY_IND_V01,967.ei = wlfw_msa_ready_ind_msg_v01_ei,968.decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01),969.fn = ath10k_qmi_msa_ready_ind,970},971{}972};973974static int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,975struct qmi_service *service)976{977struct ath10k_qmi *qmi = container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);978struct sockaddr_qrtr *sq = &qmi->sq;979struct ath10k *ar = qmi->ar;980int ret;981982sq->sq_family = AF_QIPCRTR;983sq->sq_node = service->node;984sq->sq_port = service->port;985986ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");987988ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)&qmi->sq,989sizeof(qmi->sq), 0);990if (ret) {991ath10k_err(ar, "failed to connect to a remote QMI service port\n");992return ret;993}994995ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi wifi fw qmi service connected\n");996ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_ARRIVE, NULL);997998return ret;999}10001001static void ath10k_qmi_del_server(struct qmi_handle *qmi_hdl,1002struct qmi_service *service)1003{1004struct ath10k_qmi *qmi =1005container_of(qmi_hdl, struct ath10k_qmi, qmi_hdl);10061007qmi->fw_ready = false;10081009/*1010* The del_server event is to be processed only if coming from1011* the qmi server. The qmi infrastructure sends del_server, when1012* any client releases the qmi handle. In this case do not process1013* this del_server event.1014*/1015if (qmi->state == ATH10K_QMI_STATE_INIT_DONE)1016ath10k_qmi_driver_event_post(qmi, ATH10K_QMI_EVENT_SERVER_EXIT,1017NULL);1018}10191020static const struct qmi_ops ath10k_qmi_ops = {1021.new_server = ath10k_qmi_new_server,1022.del_server = ath10k_qmi_del_server,1023};10241025static void ath10k_qmi_driver_event_work(struct work_struct *work)1026{1027struct ath10k_qmi *qmi = container_of(work, struct ath10k_qmi,1028event_work);1029struct ath10k_qmi_driver_event *event;1030struct ath10k *ar = qmi->ar;10311032spin_lock(&qmi->event_lock);1033while (!list_empty(&qmi->event_list)) {1034event = list_first_entry(&qmi->event_list,1035struct ath10k_qmi_driver_event, list);1036list_del(&event->list);1037spin_unlock(&qmi->event_lock);10381039switch (event->type) {1040case ATH10K_QMI_EVENT_SERVER_ARRIVE:1041ath10k_qmi_event_server_arrive(qmi);1042if (qmi->no_msa_ready_indicator) {1043ath10k_info(ar, "qmi not waiting for msa_ready indicator");1044ath10k_qmi_event_msa_ready(qmi);1045}1046break;1047case ATH10K_QMI_EVENT_SERVER_EXIT:1048ath10k_qmi_event_server_exit(qmi);1049break;1050case ATH10K_QMI_EVENT_FW_READY_IND:1051ath10k_qmi_event_fw_ready_ind(qmi);1052break;1053case ATH10K_QMI_EVENT_MSA_READY_IND:1054if (qmi->no_msa_ready_indicator) {1055ath10k_warn(ar, "qmi unexpected msa_ready indicator");1056break;1057}1058ath10k_qmi_event_msa_ready(qmi);1059break;1060default:1061ath10k_warn(ar, "invalid event type: %d", event->type);1062break;1063}1064kfree(event);1065spin_lock(&qmi->event_lock);1066}1067spin_unlock(&qmi->event_lock);1068}10691070int ath10k_qmi_init(struct ath10k *ar, u32 msa_size)1071{1072struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);1073struct device *dev = ar->dev;1074struct ath10k_qmi *qmi;1075int ret;10761077qmi = kzalloc(sizeof(*qmi), GFP_KERNEL);1078if (!qmi)1079return -ENOMEM;10801081qmi->ar = ar;1082ar_snoc->qmi = qmi;10831084if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm"))1085qmi->msa_fixed_perm = true;10861087if (of_property_read_bool(dev->of_node, "qcom,no-msa-ready-indicator"))1088qmi->no_msa_ready_indicator = true;10891090ret = qmi_handle_init(&qmi->qmi_hdl,1091WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,1092&ath10k_qmi_ops, qmi_msg_handler);1093if (ret)1094goto err;10951096qmi->event_wq = alloc_ordered_workqueue("ath10k_qmi_driver_event", 0);1097if (!qmi->event_wq) {1098ath10k_err(ar, "failed to allocate workqueue\n");1099ret = -EFAULT;1100goto err_release_qmi_handle;1101}11021103INIT_LIST_HEAD(&qmi->event_list);1104spin_lock_init(&qmi->event_lock);1105INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work);11061107ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01,1108WLFW_SERVICE_VERS_V01, 0);1109if (ret)1110goto err_qmi_lookup;11111112qmi->state = ATH10K_QMI_STATE_INIT_DONE;1113return 0;11141115err_qmi_lookup:1116destroy_workqueue(qmi->event_wq);11171118err_release_qmi_handle:1119qmi_handle_release(&qmi->qmi_hdl);11201121err:1122kfree(qmi);1123return ret;1124}11251126int ath10k_qmi_deinit(struct ath10k *ar)1127{1128struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);1129struct ath10k_qmi *qmi = ar_snoc->qmi;11301131qmi->state = ATH10K_QMI_STATE_DEINIT;1132qmi_handle_release(&qmi->qmi_hdl);1133cancel_work_sync(&qmi->event_work);1134destroy_workqueue(qmi->event_wq);1135kfree(qmi);1136ar_snoc->qmi = NULL;11371138return 0;1139}114011411142