Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
  3 */
  4
  5#include <linux/cleanup.h>
  6#include <linux/io.h>
  7#include <linux/errno.h>
  8#include <linux/delay.h>
  9#include <linux/mutex.h>
 10#include <linux/slab.h>
 11#include <linux/types.h>
 12#include <linux/firmware/qcom/qcom_scm.h>
 13#include <linux/firmware/qcom/qcom_tzmem.h>
 14#include <linux/arm-smccc.h>
 15#include <linux/dma-mapping.h>
 16
 17#include "qcom_scm.h"
 18
 19/**
 20 * struct arm_smccc_args
 21 * @args:	The array of values used in registers in smc instruction
 22 */
 23struct arm_smccc_args {
 24	unsigned long args[8];
 25};
 26
 27static DEFINE_MUTEX(qcom_scm_lock);
 28
 29#define QCOM_SCM_EBUSY_WAIT_MS 30
 30#define QCOM_SCM_EBUSY_MAX_RETRY 20
 31
 32#define SCM_SMC_N_REG_ARGS	4
 33#define SCM_SMC_FIRST_EXT_IDX	(SCM_SMC_N_REG_ARGS - 1)
 34#define SCM_SMC_N_EXT_ARGS	(MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1)
 35#define SCM_SMC_FIRST_REG_IDX	2
 36#define SCM_SMC_LAST_REG_IDX	(SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1)
 37
 38static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
 39			       struct arm_smccc_res *res)
 40{
 41	unsigned long a0 = smc->args[0];
 42	struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
 43
 44	quirk.state.a6 = 0;
 45
 46	do {
 47		arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
 48				    smc->args[3], smc->args[4], smc->args[5],
 49				    quirk.state.a6, smc->args[7], res, &quirk);
 50
 51		if (res->a0 == QCOM_SCM_INTERRUPTED)
 52			a0 = res->a0;
 53
 54	} while (res->a0 == QCOM_SCM_INTERRUPTED);
 55}
 56
 57static void fill_wq_resume_args(struct arm_smccc_args *resume, u32 smc_call_ctx)
 58{
 59	memset(resume->args, 0, sizeof(resume->args[0]) * ARRAY_SIZE(resume->args));
 60
 61	resume->args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
 62					ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP,
 63					SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_RESUME));
 64
 65	resume->args[1] = QCOM_SCM_ARGS(1);
 66
 67	resume->args[2] = smc_call_ctx;
 68}
 69
 70int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending)
 71{
 72	int ret;
 73	struct arm_smccc_res get_wq_res;
 74	struct arm_smccc_args get_wq_ctx = {0};
 75
 76	get_wq_ctx.args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,
 77				ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP,
 78				SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_GET_WQ_CTX));
 79
 80	/* Guaranteed to return only success or error, no WAITQ_* */
 81	__scm_smc_do_quirk(&get_wq_ctx, &get_wq_res);
 82	ret = get_wq_res.a0;
 83	if (ret)
 84		return ret;
 85
 86	*wq_ctx = get_wq_res.a1;
 87	*flags  = get_wq_res.a2;
 88	*more_pending = get_wq_res.a3;
 89
 90	return 0;
 91}
 92
 93static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_args *waitq,
 94					   struct arm_smccc_res *res)
 95{
 96	int ret;
 97	u32 wq_ctx, smc_call_ctx;
 98	struct arm_smccc_args resume;
 99	struct arm_smccc_args *smc = waitq;
100
101	do {
102		__scm_smc_do_quirk(smc, res);
103
104		if (res->a0 == QCOM_SCM_WAITQ_SLEEP) {
105			wq_ctx = res->a1;
106			smc_call_ctx = res->a2;
107
108			ret = qcom_scm_wait_for_wq_completion(wq_ctx);
109			if (ret)
110				return ret;
111
112			fill_wq_resume_args(&resume, smc_call_ctx);
113			smc = &resume;
114		}
115	} while (res->a0 == QCOM_SCM_WAITQ_SLEEP);
116
117	return 0;
118}
119
120static int __scm_smc_do(struct device *dev, struct arm_smccc_args *smc,
121			struct arm_smccc_res *res, bool atomic)
122{
123	int ret, retry_count = 0;
124
125	if (atomic) {
126		__scm_smc_do_quirk(smc, res);
127		return 0;
128	}
129
130	do {
131		mutex_lock(&qcom_scm_lock);
132
133		ret = __scm_smc_do_quirk_handle_waitq(dev, smc, res);
134
135		mutex_unlock(&qcom_scm_lock);
136
137		if (ret)
138			return ret;
139
140		if (res->a0 == QCOM_SCM_V2_EBUSY) {
141			if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
142				break;
143			msleep(QCOM_SCM_EBUSY_WAIT_MS);
144		}
145	}  while (res->a0 == QCOM_SCM_V2_EBUSY);
146
147	return 0;
148}
149
150
151int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
152		   enum qcom_scm_convention qcom_convention,
153		   struct qcom_scm_res *res, bool atomic)
154{
155	struct qcom_tzmem_pool *mempool = qcom_scm_get_tzmem_pool();
156	int arglen = desc->arginfo & 0xf;
157	int i, ret;
158	void *args_virt __free(qcom_tzmem) = NULL;
159	gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
160	u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
161	u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ?
162				    ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
163	struct arm_smccc_res smc_res;
164	struct arm_smccc_args smc = {0};
165
166	smc.args[0] = ARM_SMCCC_CALL_VAL(
167		smccc_call_type,
168		qcom_smccc_convention,
169		desc->owner,
170		SCM_SMC_FNID(desc->svc, desc->cmd));
171	smc.args[1] = desc->arginfo;
172	for (i = 0; i < SCM_SMC_N_REG_ARGS; i++)
173		smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i];
174
175	if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) {
176		if (!mempool)
177			return -EINVAL;
178
179		args_virt = qcom_tzmem_alloc(mempool,
180					     SCM_SMC_N_EXT_ARGS * sizeof(u64),
181					     flag);
182		if (!args_virt)
183			return -ENOMEM;
184
185		if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
186			__le32 *args = args_virt;
187
188			for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
189				args[i] = cpu_to_le32(desc->args[i +
190						      SCM_SMC_FIRST_EXT_IDX]);
191		} else {
192			__le64 *args = args_virt;
193
194			for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
195				args[i] = cpu_to_le64(desc->args[i +
196						      SCM_SMC_FIRST_EXT_IDX]);
197		}
198
199		smc.args[SCM_SMC_LAST_REG_IDX] = qcom_tzmem_to_phys(args_virt);
200	}
201
202	ret = __scm_smc_do(dev, &smc, &smc_res, atomic);
203	if (ret)
204		return ret;
205
206	if (res) {
207		res->result[0] = smc_res.a1;
208		res->result[1] = smc_res.a2;
209		res->result[2] = smc_res.a3;
210	}
211
212	return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
213
214}