// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/module.h>
#include <linux/net.h>
#include <linux/platform_device.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/soc/qcom/smem.h>
#include <linux/string.h>
#include <net/sock.h>
#include "debug.h"
#include "snoc.h"
#define ATH10K_QMI_CLIENT_ID 0x4b4e454c
#define ATH10K_QMI_TIMEOUT 30
#define SMEM_IMAGE_VERSION_TABLE 469
#define SMEM_IMAGE_TABLE_CNSS_INDEX 13
#define SMEM_IMAGE_VERSION_ENTRY_SIZE 128
#define SMEM_IMAGE_VERSION_NAME_SIZE 75
static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,
struct ath10k_msa_mem_info *mem_info)
{
struct qcom_scm_vmperm dst_perms[3];
struct ath10k *ar = qmi->ar;
u64 src_perms;
u32 perm_count;
int ret;
src_perms = BIT(QCOM_SCM_VMID_HLOS);
dst_perms[0].vmid = QCOM_SCM_VMID_MSS_MSA;
dst_perms[0].perm = QCOM_SCM_PERM_RW;
dst_perms[1].vmid = QCOM_SCM_VMID_WLAN;
dst_perms[1].perm = QCOM_SCM_PERM_RW;
if (mem_info->secure) {
perm_count = 2;
} else {
dst_perms[2].vmid = QCOM_SCM_VMID_WLAN_CE;
dst_perms[2].perm = QCOM_SCM_PERM_RW;
perm_count = 3;
}
ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
&src_perms, dst_perms, perm_count);
if (ret < 0)
ath10k_err(ar, "failed to assign msa map permissions: %d\n", ret);
return ret;
}
static int ath10k_qmi_unmap_msa_permission(struct ath10k_qmi *qmi,
struct ath10k_msa_mem_info *mem_info)
{
struct qcom_scm_vmperm dst_perms;
struct ath10k *ar = qmi->ar;
u64 src_perms;
int ret;
src_perms = BIT(QCOM_SCM_VMID_MSS_MSA) | BIT(QCOM_SCM_VMID_WLAN);
if (!mem_info->secure)
src_perms |= BIT(QCOM_SCM_VMID_WLAN_CE);
dst_perms.vmid = QCOM_SCM_VMID_HLOS;
dst_perms.perm = QCOM_SCM_PERM_RW;
ret = qcom_scm_assign_mem(mem_info->addr, mem_info->size,
&src_perms, &dst_perms, 1);
if (ret < 0)
ath10k_err(ar, "failed to unmap msa permissions: %d\n", ret);
return ret;
}
static int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi)
{
int ret;
int i;
if (qmi->msa_fixed_perm)
return 0;
for (i = 0; i < qmi->nr_mem_region; i++) {
ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]);
if (ret)
goto err_unmap;
}
return 0;
err_unmap:
for (i--; i >= 0; i--)
ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
return ret;
}
static void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi)
{
int i;
if (qmi->msa_fixed_perm)
return;
for (i = 0; i < qmi->nr_mem_region; i++)
ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]);
}
static int ath10k_qmi_msa_mem_info_send_sync_msg(struct ath10k_qmi *qmi)
{
struct wlfw_msa_info_resp_msg_v01 resp = {};
struct wlfw_msa_info_req_msg_v01 req = {};
struct ath10k *ar = qmi->ar;
phys_addr_t max_mapped_addr;
struct qmi_txn txn;
int ret;
int i;
req.msa_addr = ar->msa.paddr;
req.size = ar->msa.mem_size;
ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
wlfw_msa_info_resp_msg_v01_ei, &resp);
if (ret < 0)
goto out;
ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
QMI_WLFW_MSA_INFO_REQ_V01,
WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,
wlfw_msa_info_req_msg_v01_ei, &req);
if (ret < 0) {
qmi_txn_cancel(&txn);
ath10k_err(ar, "failed to send msa mem info req: %d\n", ret);
goto