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