xref: /openbmc/linux/drivers/firmware/qcom_scm-legacy.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
19a434ceeSElliot Berman // SPDX-License-Identifier: GPL-2.0-only
29a434ceeSElliot Berman /* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
39a434ceeSElliot Berman  * Copyright (C) 2015 Linaro Ltd.
49a434ceeSElliot Berman  */
59a434ceeSElliot Berman 
69a434ceeSElliot Berman #include <linux/slab.h>
79a434ceeSElliot Berman #include <linux/io.h>
89a434ceeSElliot Berman #include <linux/module.h>
99a434ceeSElliot Berman #include <linux/mutex.h>
109a434ceeSElliot Berman #include <linux/errno.h>
119a434ceeSElliot Berman #include <linux/err.h>
12*3bf90ecaSElliot Berman #include <linux/firmware/qcom/qcom_scm.h>
139a434ceeSElliot Berman #include <linux/arm-smccc.h>
149a434ceeSElliot Berman #include <linux/dma-mapping.h>
159a434ceeSElliot Berman 
169a434ceeSElliot Berman #include "qcom_scm.h"
179a434ceeSElliot Berman 
189a434ceeSElliot Berman static DEFINE_MUTEX(qcom_scm_lock);
199a434ceeSElliot Berman 
209a434ceeSElliot Berman 
219a434ceeSElliot Berman /**
229a434ceeSElliot Berman  * struct arm_smccc_args
239a434ceeSElliot Berman  * @args:	The array of values used in registers in smc instruction
249a434ceeSElliot Berman  */
259a434ceeSElliot Berman struct arm_smccc_args {
269a434ceeSElliot Berman 	unsigned long args[8];
279a434ceeSElliot Berman };
289a434ceeSElliot Berman 
299a434ceeSElliot Berman 
309a434ceeSElliot Berman /**
319a434ceeSElliot Berman  * struct scm_legacy_command - one SCM command buffer
329a434ceeSElliot Berman  * @len: total available memory for command and response
339a434ceeSElliot Berman  * @buf_offset: start of command buffer
349a434ceeSElliot Berman  * @resp_hdr_offset: start of response buffer
359a434ceeSElliot Berman  * @id: command to be executed
369a434ceeSElliot Berman  * @buf: buffer returned from scm_legacy_get_command_buffer()
379a434ceeSElliot Berman  *
389a434ceeSElliot Berman  * An SCM command is laid out in memory as follows:
399a434ceeSElliot Berman  *
409a434ceeSElliot Berman  *	------------------- <--- struct scm_legacy_command
419a434ceeSElliot Berman  *	| command header  |
429a434ceeSElliot Berman  *	------------------- <--- scm_legacy_get_command_buffer()
439a434ceeSElliot Berman  *	| command buffer  |
449a434ceeSElliot Berman  *	------------------- <--- struct scm_legacy_response and
459a434ceeSElliot Berman  *	| response header |      scm_legacy_command_to_response()
469a434ceeSElliot Berman  *	------------------- <--- scm_legacy_get_response_buffer()
479a434ceeSElliot Berman  *	| response buffer |
489a434ceeSElliot Berman  *	-------------------
499a434ceeSElliot Berman  *
509a434ceeSElliot Berman  * There can be arbitrary padding between the headers and buffers so
519a434ceeSElliot Berman  * you should always use the appropriate scm_legacy_get_*_buffer() routines
529a434ceeSElliot Berman  * to access the buffers in a safe manner.
539a434ceeSElliot Berman  */
549a434ceeSElliot Berman struct scm_legacy_command {
559a434ceeSElliot Berman 	__le32 len;
569a434ceeSElliot Berman 	__le32 buf_offset;
579a434ceeSElliot Berman 	__le32 resp_hdr_offset;
589a434ceeSElliot Berman 	__le32 id;
59c2097772SGustavo A. R. Silva 	__le32 buf[];
609a434ceeSElliot Berman };
619a434ceeSElliot Berman 
629a434ceeSElliot Berman /**
639a434ceeSElliot Berman  * struct scm_legacy_response - one SCM response buffer
649a434ceeSElliot Berman  * @len: total available memory for response
659a434ceeSElliot Berman  * @buf_offset: start of response data relative to start of scm_legacy_response
669a434ceeSElliot Berman  * @is_complete: indicates if the command has finished processing
679a434ceeSElliot Berman  */
689a434ceeSElliot Berman struct scm_legacy_response {
699a434ceeSElliot Berman 	__le32 len;
709a434ceeSElliot Berman 	__le32 buf_offset;
719a434ceeSElliot Berman 	__le32 is_complete;
729a434ceeSElliot Berman };
739a434ceeSElliot Berman 
749a434ceeSElliot Berman /**
759a434ceeSElliot Berman  * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response
769a434ceeSElliot Berman  * @cmd: command
779a434ceeSElliot Berman  *
789a434ceeSElliot Berman  * Returns a pointer to a response for a command.
799a434ceeSElliot Berman  */
scm_legacy_command_to_response(const struct scm_legacy_command * cmd)809a434ceeSElliot Berman static inline struct scm_legacy_response *scm_legacy_command_to_response(
819a434ceeSElliot Berman 		const struct scm_legacy_command *cmd)
829a434ceeSElliot Berman {
839a434ceeSElliot Berman 	return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
849a434ceeSElliot Berman }
859a434ceeSElliot Berman 
869a434ceeSElliot Berman /**
879a434ceeSElliot Berman  * scm_legacy_get_command_buffer() - Get a pointer to a command buffer
889a434ceeSElliot Berman  * @cmd: command
899a434ceeSElliot Berman  *
909a434ceeSElliot Berman  * Returns a pointer to the command buffer of a command.
919a434ceeSElliot Berman  */
scm_legacy_get_command_buffer(const struct scm_legacy_command * cmd)929a434ceeSElliot Berman static inline void *scm_legacy_get_command_buffer(
939a434ceeSElliot Berman 		const struct scm_legacy_command *cmd)
949a434ceeSElliot Berman {
959a434ceeSElliot Berman 	return (void *)cmd->buf;
969a434ceeSElliot Berman }
979a434ceeSElliot Berman 
989a434ceeSElliot Berman /**
999a434ceeSElliot Berman  * scm_legacy_get_response_buffer() - Get a pointer to a response buffer
1009a434ceeSElliot Berman  * @rsp: response
1019a434ceeSElliot Berman  *
1029a434ceeSElliot Berman  * Returns a pointer to a response buffer of a response.
1039a434ceeSElliot Berman  */
scm_legacy_get_response_buffer(const struct scm_legacy_response * rsp)1049a434ceeSElliot Berman static inline void *scm_legacy_get_response_buffer(
1059a434ceeSElliot Berman 		const struct scm_legacy_response *rsp)
1069a434ceeSElliot Berman {
1079a434ceeSElliot Berman 	return (void *)rsp + le32_to_cpu(rsp->buf_offset);
1089a434ceeSElliot Berman }
1099a434ceeSElliot Berman 
__scm_legacy_do(const struct arm_smccc_args * smc,struct arm_smccc_res * res)1109a434ceeSElliot Berman static void __scm_legacy_do(const struct arm_smccc_args *smc,
1119a434ceeSElliot Berman 			    struct arm_smccc_res *res)
1129a434ceeSElliot Berman {
1139a434ceeSElliot Berman 	do {
1149a434ceeSElliot Berman 		arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2],
1159a434ceeSElliot Berman 			      smc->args[3], smc->args[4], smc->args[5],
1169a434ceeSElliot Berman 			      smc->args[6], smc->args[7], res);
1179a434ceeSElliot Berman 	} while (res->a0 == QCOM_SCM_INTERRUPTED);
1189a434ceeSElliot Berman }
1199a434ceeSElliot Berman 
1209a434ceeSElliot Berman /**
121e1cd92daSStephen Boyd  * scm_legacy_call() - Sends a command to the SCM and waits for the command to
1229a434ceeSElliot Berman  * finish processing.
123ebf21bbcSKrzysztof Kozlowski  * @dev:	device
124ebf21bbcSKrzysztof Kozlowski  * @desc:	descriptor structure containing arguments and return values
125ebf21bbcSKrzysztof Kozlowski  * @res:        results from SMC call
1269a434ceeSElliot Berman  *
1279a434ceeSElliot Berman  * A note on cache maintenance:
1289a434ceeSElliot Berman  * Note that any buffers that are expected to be accessed by the secure world
1299a434ceeSElliot Berman  * must be flushed before invoking qcom_scm_call and invalidated in the cache
1309a434ceeSElliot Berman  * immediately after qcom_scm_call returns. Cache maintenance on the command
1319a434ceeSElliot Berman  * and response buffers is taken care of by qcom_scm_call; however, callers are
1329a434ceeSElliot Berman  * responsible for any other cached buffers passed over to the secure world.
1339a434ceeSElliot Berman  */
scm_legacy_call(struct device * dev,const struct qcom_scm_desc * desc,struct qcom_scm_res * res)1349a434ceeSElliot Berman int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
1359a434ceeSElliot Berman 		    struct qcom_scm_res *res)
1369a434ceeSElliot Berman {
1379a434ceeSElliot Berman 	u8 arglen = desc->arginfo & 0xf;
1389a434ceeSElliot Berman 	int ret = 0, context_id;
1399a434ceeSElliot Berman 	unsigned int i;
1409a434ceeSElliot Berman 	struct scm_legacy_command *cmd;
1419a434ceeSElliot Berman 	struct scm_legacy_response *rsp;
1429a434ceeSElliot Berman 	struct arm_smccc_args smc = {0};
1439a434ceeSElliot Berman 	struct arm_smccc_res smc_res;
1449a434ceeSElliot Berman 	const size_t cmd_len = arglen * sizeof(__le32);
1459a434ceeSElliot Berman 	const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32);
1469a434ceeSElliot Berman 	size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
1479a434ceeSElliot Berman 	dma_addr_t cmd_phys;
1489a434ceeSElliot Berman 	__le32 *arg_buf;
1499a434ceeSElliot Berman 	const __le32 *res_buf;
1509a434ceeSElliot Berman 
1519a434ceeSElliot Berman 	cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
1529a434ceeSElliot Berman 	if (!cmd)
1539a434ceeSElliot Berman 		return -ENOMEM;
1549a434ceeSElliot Berman 
1559a434ceeSElliot Berman 	cmd->len = cpu_to_le32(alloc_len);
1569a434ceeSElliot Berman 	cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
1579a434ceeSElliot Berman 	cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
1589a434ceeSElliot Berman 	cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd));
1599a434ceeSElliot Berman 
1609a434ceeSElliot Berman 	arg_buf = scm_legacy_get_command_buffer(cmd);
1619a434ceeSElliot Berman 	for (i = 0; i < arglen; i++)
1629a434ceeSElliot Berman 		arg_buf[i] = cpu_to_le32(desc->args[i]);
1639a434ceeSElliot Berman 
1649a434ceeSElliot Berman 	rsp = scm_legacy_command_to_response(cmd);
1659a434ceeSElliot Berman 
1669a434ceeSElliot Berman 	cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
1679a434ceeSElliot Berman 	if (dma_mapping_error(dev, cmd_phys)) {
1689a434ceeSElliot Berman 		kfree(cmd);
1699a434ceeSElliot Berman 		return -ENOMEM;
1709a434ceeSElliot Berman 	}
1719a434ceeSElliot Berman 
1729a434ceeSElliot Berman 	smc.args[0] = 1;
1739a434ceeSElliot Berman 	smc.args[1] = (unsigned long)&context_id;
1749a434ceeSElliot Berman 	smc.args[2] = cmd_phys;
1759a434ceeSElliot Berman 
1769a434ceeSElliot Berman 	mutex_lock(&qcom_scm_lock);
1779a434ceeSElliot Berman 	__scm_legacy_do(&smc, &smc_res);
1789a434ceeSElliot Berman 	if (smc_res.a0)
1799a434ceeSElliot Berman 		ret = qcom_scm_remap_error(smc_res.a0);
1809a434ceeSElliot Berman 	mutex_unlock(&qcom_scm_lock);
1819a434ceeSElliot Berman 	if (ret)
1829a434ceeSElliot Berman 		goto out;
1839a434ceeSElliot Berman 
1849a434ceeSElliot Berman 	do {
1859a434ceeSElliot Berman 		dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
1869a434ceeSElliot Berman 					sizeof(*rsp), DMA_FROM_DEVICE);
1879a434ceeSElliot Berman 	} while (!rsp->is_complete);
1889a434ceeSElliot Berman 
1899a434ceeSElliot Berman 	dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
1909a434ceeSElliot Berman 				le32_to_cpu(rsp->buf_offset),
1919a434ceeSElliot Berman 				resp_len, DMA_FROM_DEVICE);
1929a434ceeSElliot Berman 
1939a434ceeSElliot Berman 	if (res) {
1949a434ceeSElliot Berman 		res_buf = scm_legacy_get_response_buffer(rsp);
1959a434ceeSElliot Berman 		for (i = 0; i < MAX_QCOM_SCM_RETS; i++)
1969a434ceeSElliot Berman 			res->result[i] = le32_to_cpu(res_buf[i]);
1979a434ceeSElliot Berman 	}
1989a434ceeSElliot Berman out:
1999a434ceeSElliot Berman 	dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
2009a434ceeSElliot Berman 	kfree(cmd);
2019a434ceeSElliot Berman 	return ret;
2029a434ceeSElliot Berman }
2039a434ceeSElliot Berman 
2049a434ceeSElliot Berman #define SCM_LEGACY_ATOMIC_N_REG_ARGS	5
2059a434ceeSElliot Berman #define SCM_LEGACY_ATOMIC_FIRST_REG_IDX	2
2069a434ceeSElliot Berman #define SCM_LEGACY_CLASS_REGISTER		(0x2 << 8)
2079a434ceeSElliot Berman #define SCM_LEGACY_MASK_IRQS		BIT(5)
2089a434ceeSElliot Berman #define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \
2099a434ceeSElliot Berman 				((SCM_LEGACY_FNID(svc, cmd) << 12) | \
2109a434ceeSElliot Berman 				SCM_LEGACY_CLASS_REGISTER | \
2119a434ceeSElliot Berman 				SCM_LEGACY_MASK_IRQS | \
2129a434ceeSElliot Berman 				(n & 0xf))
2139a434ceeSElliot Berman 
2149a434ceeSElliot Berman /**
215e1cd92daSStephen Boyd  * scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments
2169a434ceeSElliot Berman  * and 3 return values
217ebf21bbcSKrzysztof Kozlowski  * @unused: device, legacy argument, not used, can be NULL
2189a434ceeSElliot Berman  * @desc: SCM call descriptor containing arguments
2199a434ceeSElliot Berman  * @res:  SCM call return values
2209a434ceeSElliot Berman  *
2219a434ceeSElliot Berman  * This shall only be used with commands that are guaranteed to be
2229a434ceeSElliot Berman  * uninterruptable, atomic and SMP safe.
2239a434ceeSElliot Berman  */
scm_legacy_call_atomic(struct device * unused,const struct qcom_scm_desc * desc,struct qcom_scm_res * res)2249a434ceeSElliot Berman int scm_legacy_call_atomic(struct device *unused,
2259a434ceeSElliot Berman 			   const struct qcom_scm_desc *desc,
2269a434ceeSElliot Berman 			   struct qcom_scm_res *res)
2279a434ceeSElliot Berman {
2289a434ceeSElliot Berman 	int context_id;
2299a434ceeSElliot Berman 	struct arm_smccc_res smc_res;
2309a434ceeSElliot Berman 	size_t arglen = desc->arginfo & 0xf;
2319a434ceeSElliot Berman 
2329a434ceeSElliot Berman 	BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS);
2339a434ceeSElliot Berman 
2349a434ceeSElliot Berman 	arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen),
2359a434ceeSElliot Berman 		      (unsigned long)&context_id,
2369a434ceeSElliot Berman 		      desc->args[0], desc->args[1], desc->args[2],
2379a434ceeSElliot Berman 		      desc->args[3], desc->args[4], 0, &smc_res);
2389a434ceeSElliot Berman 
2399a434ceeSElliot Berman 	if (res) {
2409a434ceeSElliot Berman 		res->result[0] = smc_res.a1;
2419a434ceeSElliot Berman 		res->result[1] = smc_res.a2;
2429a434ceeSElliot Berman 		res->result[2] = smc_res.a3;
2439a434ceeSElliot Berman 	}
2449a434ceeSElliot Berman 
2459a434ceeSElliot Berman 	return smc_res.a0;
2469a434ceeSElliot Berman }
247