Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b869aae8 authored by Vivek Gautam's avatar Vivek Gautam Committed by Alistair Delva
Browse files

UPSTREAM: firmware: qcom_scm-64: Add atomic version of qcom_scm_call

There are scnenarios where drivers are required to make a
scm call in atomic context, such as in one of the qcom's
arm-smmu-500 errata [1].

[1] ("https://source.codeaurora.org/quic/la/kernel/msm-4.9/


      tree/drivers/iommu/arm-smmu.c?h=msm-4.9#n4842")

Signed-off-by: default avatarVivek Gautam <vivek.gautam@codeaurora.org>
Reviewed-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
Reviewed-by: default avatarStephen Boyd <swboyd@chromium.org>
Acked-by: default avatarAndy Gross <agross@kernel.org>
Signed-off-by: default avatarSai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
Signed-off-by: default avatarWill Deacon <will@kernel.org>
(cherry picked from commit 1a5ea3b7a6ac6c133660b9aeda95b2087c8eec47)
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
Bug: 146449535
Change-Id: Ic9c14f2334271d23b04293e1cebdaaf6a1fbbe9d
parent 9df826d6
Loading
Loading
Loading
Loading
+94 −44
Original line number Diff line number Diff line
@@ -62,32 +62,72 @@ static DEFINE_MUTEX(qcom_scm_lock);
#define FIRST_EXT_ARG_IDX 3
#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1)

/**
 * qcom_scm_call() - Invoke a syscall in the secure world
 * @dev:	device
 * @svc_id:	service identifier
 * @cmd_id:	command identifier
 * @desc:	Descriptor structure containing arguments and return values
 *
 * Sends a command to the SCM and waits for the command to finish processing.
 * This should *only* be called in pre-emptible context.
*/
static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
static void __qcom_scm_call_do(const struct qcom_scm_desc *desc,
			       struct arm_smccc_res *res, u32 fn_id,
			       u64 x5, u32 type)
{
	u64 cmd;
	struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };

	cmd = ARM_SMCCC_CALL_VAL(type, qcom_smccc_convention,
				 ARM_SMCCC_OWNER_SIP, fn_id);

	quirk.state.a6 = 0;

	do {
		arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0],
				    desc->args[1], desc->args[2], x5,
				    quirk.state.a6, 0, res, &quirk);

		if (res->a0 == QCOM_SCM_INTERRUPTED)
			cmd = res->a0;

	} while (res->a0 == QCOM_SCM_INTERRUPTED);
}

static void qcom_scm_call_do(const struct qcom_scm_desc *desc,
			     struct arm_smccc_res *res, u32 fn_id,
			     u64 x5, bool atomic)
{
	int retry_count = 0;

	if (atomic) {
		__qcom_scm_call_do(desc, res, fn_id, x5, ARM_SMCCC_FAST_CALL);
		return;
	}

	do {
		mutex_lock(&qcom_scm_lock);

		__qcom_scm_call_do(desc, res, fn_id, x5,
				   ARM_SMCCC_STD_CALL);

		mutex_unlock(&qcom_scm_lock);

		if (res->a0 == QCOM_SCM_V2_EBUSY) {
			if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
				break;
			msleep(QCOM_SCM_EBUSY_WAIT_MS);
		}
	}  while (res->a0 == QCOM_SCM_V2_EBUSY);
}

static int ___qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
			    const struct qcom_scm_desc *desc,
			 struct arm_smccc_res *res)
			    struct arm_smccc_res *res, bool atomic)
{
	int arglen = desc->arginfo & 0xf;
	int retry_count = 0, i;
	int i;
	u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id);
	u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX];
	u64 x5 = desc->args[FIRST_EXT_ARG_IDX];
	dma_addr_t args_phys = 0;
	void *args_virt = NULL;
	size_t alloc_len;
	struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6};
	gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;

	if (unlikely(arglen > N_REGISTER_ARGS)) {
		alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
		args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
		args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag);

		if (!args_virt)
			return -ENOMEM;
@@ -117,33 +157,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
		x5 = args_phys;
	}

	do {
		mutex_lock(&qcom_scm_lock);

		cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
					 qcom_smccc_convention,
					 ARM_SMCCC_OWNER_SIP, fn_id);

		quirk.state.a6 = 0;

		do {
			arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0],
				      desc->args[1], desc->args[2], x5,
				      quirk.state.a6, 0, res, &quirk);

			if (res->a0 == QCOM_SCM_INTERRUPTED)
				cmd = res->a0;

		} while (res->a0 == QCOM_SCM_INTERRUPTED);

		mutex_unlock(&qcom_scm_lock);

		if (res->a0 == QCOM_SCM_V2_EBUSY) {
			if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
				break;
			msleep(QCOM_SCM_EBUSY_WAIT_MS);
		}
	}  while (res->a0 == QCOM_SCM_V2_EBUSY);
	qcom_scm_call_do(desc, res, fn_id, x5, atomic);

	if (args_virt) {
		dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
@@ -156,6 +170,42 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
	return 0;
}

/**
 * qcom_scm_call() - Invoke a syscall in the secure world
 * @dev:	device
 * @svc_id:	service identifier
 * @cmd_id:	command identifier
 * @desc:	Descriptor structure containing arguments and return values
 *
 * Sends a command to the SCM and waits for the command to finish processing.
 * This should *only* be called in pre-emptible context.
 */
static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
			 const struct qcom_scm_desc *desc,
			 struct arm_smccc_res *res)
{
	might_sleep();
	return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, false);
}

/**
 * qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
 * @dev:	device
 * @svc_id:	service identifier
 * @cmd_id:	command identifier
 * @desc:	Descriptor structure containing arguments and return values
 * @res:	Structure containing results from SMC/HVC call
 *
 * Sends a command to the SCM and waits for the command to finish processing.
 * This can be called in atomic context.
 */
static int qcom_scm_call_atomic(struct device *dev, u32 svc_id, u32 cmd_id,
				const struct qcom_scm_desc *desc,
				struct arm_smccc_res *res)
{
	return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, true);
}

/**
 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
 * @entry: Entry point function for the cpus