From 9a434cee773ae15309ac225f27551b5492618e4a Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Tue, 7 Jan 2020 13:04:26 -0800 Subject: firmware: qcom_scm: Dynamically support SMCCC and legacy conventions Dynamically support SMCCCC and legacy conventions by detecting which convention to use at runtime. qcom_scm_call_atomic and qcom_scm_call can then be moved in qcom_scm.c and use underlying convention backend as appropriate. Thus, rename qcom_scm-64,-32 to reflect that they are backends for -smc and -legacy, respectively. Also add support for making SCM calls earlier than when SCM driver probes to support use cases such as qcom_scm_set_cold_boot_addr. Support is added by lazily initializing the convention and guarding the query with a spin lock. The limitation of these early SCM calls is that they cannot use DMA, as in the case of >4 arguments for SMC convention and any non-atomic call for legacy convention. Tested-by: Brian Masney # arm32 Tested-by: Stephan Gerhold Signed-off-by: Elliot Berman Link: https://lore.kernel.org/r/1578431066-19600-18-git-send-email-eberman@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm-32.c | 263 ----------------------------------------- 1 file changed, 263 deletions(-) delete mode 100644 drivers/firmware/qcom_scm-32.c (limited to 'drivers/firmware/qcom_scm-32.c') diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c deleted file mode 100644 index 08220e7f2167..000000000000 --- a/drivers/firmware/qcom_scm-32.c +++ /dev/null @@ -1,263 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. - * Copyright (C) 2015 Linaro Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qcom_scm.h" - -static DEFINE_MUTEX(qcom_scm_lock); - - -/** - * struct arm_smccc_args - * @args: The array of values used in registers in smc instruction - */ -struct arm_smccc_args { - unsigned long args[8]; -}; - -#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff)) - -/** - * struct scm_legacy_command - one SCM command buffer - * @len: total available memory for command and response - * @buf_offset: start of command buffer - * @resp_hdr_offset: start of response buffer - * @id: command to be executed - * @buf: buffer returned from scm_legacy_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct scm_legacy_command - * | command header | - * ------------------- <--- scm_legacy_get_command_buffer() - * | command buffer | - * ------------------- <--- struct scm_legacy_response and - * | response header | scm_legacy_command_to_response() - * ------------------- <--- scm_legacy_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate scm_legacy_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct scm_legacy_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[0]; -}; - -/** - * struct scm_legacy_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of scm_legacy_response - * @is_complete: indicates if the command has finished processing - */ -struct scm_legacy_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct scm_legacy_response *scm_legacy_command_to_response( - const struct scm_legacy_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * scm_legacy_get_command_buffer() - Get a pointer to a command buffer - * @cmd: command - * - * Returns a pointer to the command buffer of a command. - */ -static inline void *scm_legacy_get_command_buffer( - const struct scm_legacy_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * scm_legacy_get_response_buffer() - Get a pointer to a response buffer - * @rsp: response - * - * Returns a pointer to a response buffer of a response. - */ -static inline void *scm_legacy_get_response_buffer( - const struct scm_legacy_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static void __scm_legacy_do(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - do { - arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], - smc->args[3], smc->args[4], smc->args[5], - smc->args[6], smc->args[7], res); - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -/** - * qcom_scm_call() - Sends a command to the SCM and waits for the command to - * finish processing. - * - * A note on cache maintenance: - * Note that any buffers that are expected to be accessed by the secure world - * must be flushed before invoking qcom_scm_call and invalidated in the cache - * immediately after qcom_scm_call returns. Cache maintenance on the command - * and response buffers is taken care of by qcom_scm_call; however, callers are - * responsible for any other cached buffers passed over to the secure world. - */ -int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - u8 arglen = desc->arginfo & 0xf; - int ret = 0, context_id; - unsigned int i; - struct scm_legacy_command *cmd; - struct scm_legacy_response *rsp; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res smc_res; - const size_t cmd_len = arglen * sizeof(__le32); - const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); - size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; - dma_addr_t cmd_phys; - __le32 *arg_buf; - const __le32 *res_buf; - - cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - cmd->len = cpu_to_le32(alloc_len); - cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); - cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); - cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); - - arg_buf = scm_legacy_get_command_buffer(cmd); - for (i = 0; i < arglen; i++) - arg_buf[i] = cpu_to_le32(desc->args[i]); - - rsp = scm_legacy_command_to_response(cmd); - - cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, cmd_phys)) { - kfree(cmd); - return -ENOMEM; - } - - smc.args[0] = 1; - smc.args[1] = (unsigned long)&context_id; - smc.args[2] = cmd_phys; - - mutex_lock(&qcom_scm_lock); - __scm_legacy_do(&smc, &smc_res); - if (smc_res.a0) - ret = qcom_scm_remap_error(smc_res.a0); - mutex_unlock(&qcom_scm_lock); - if (ret) - goto out; - - do { - dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, - sizeof(*rsp), DMA_FROM_DEVICE); - } while (!rsp->is_complete); - - dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + - le32_to_cpu(rsp->buf_offset), - resp_len, DMA_FROM_DEVICE); - - if (res) { - res_buf = scm_legacy_get_response_buffer(rsp); - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) - res->result[i] = le32_to_cpu(res_buf[i]); - } -out: - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); - kfree(cmd); - return ret; -} - -#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 -#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 -#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) -#define SCM_LEGACY_MASK_IRQS BIT(5) -#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ - ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ - SCM_LEGACY_CLASS_REGISTER | \ - SCM_LEGACY_MASK_IRQS | \ - (n & 0xf)) - -/** - * qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments - * and 3 return values - * @desc: SCM call descriptor containing arguments - * @res: SCM call return values - * - * This shall only be used with commands that are guaranteed to be - * uninterruptable, atomic and SMP safe. - */ -int qcom_scm_call_atomic(struct device *unused, - const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - int context_id; - struct arm_smccc_res smc_res; - size_t arglen = desc->arginfo & 0xf; - - BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); - - arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), - (unsigned long)&context_id, - desc->args[0], desc->args[1], desc->args[2], - desc->args[3], desc->args[4], 0, &smc_res); - - if (res) { - res->result[0] = smc_res.a1; - res->result[1] = smc_res.a2; - res->result[2] = smc_res.a3; - } - - return smc_res.a0; -} - -int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_INFO, - .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, - .args[0] = SCM_LEGACY_FNID(svc_id, cmd_id), - .arginfo = QCOM_SCM_ARGS(1), - }; - struct qcom_scm_res res; - - ret = qcom_scm_call(dev, &desc, &res); - - return ret ? : res.result[0]; -} - -void __qcom_scm_init(void) -{ -} -- cgit v1.2.3