18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h> 138c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "qcom_scm.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(qcom_scm_lock); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/** 228c2ecf20Sopenharmony_ci * struct arm_smccc_args 238c2ecf20Sopenharmony_ci * @args: The array of values used in registers in smc instruction 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistruct arm_smccc_args { 268c2ecf20Sopenharmony_ci unsigned long args[8]; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * struct scm_legacy_command - one SCM command buffer 328c2ecf20Sopenharmony_ci * @len: total available memory for command and response 338c2ecf20Sopenharmony_ci * @buf_offset: start of command buffer 348c2ecf20Sopenharmony_ci * @resp_hdr_offset: start of response buffer 358c2ecf20Sopenharmony_ci * @id: command to be executed 368c2ecf20Sopenharmony_ci * @buf: buffer returned from scm_legacy_get_command_buffer() 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * An SCM command is laid out in memory as follows: 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * ------------------- <--- struct scm_legacy_command 418c2ecf20Sopenharmony_ci * | command header | 428c2ecf20Sopenharmony_ci * ------------------- <--- scm_legacy_get_command_buffer() 438c2ecf20Sopenharmony_ci * | command buffer | 448c2ecf20Sopenharmony_ci * ------------------- <--- struct scm_legacy_response and 458c2ecf20Sopenharmony_ci * | response header | scm_legacy_command_to_response() 468c2ecf20Sopenharmony_ci * ------------------- <--- scm_legacy_get_response_buffer() 478c2ecf20Sopenharmony_ci * | response buffer | 488c2ecf20Sopenharmony_ci * ------------------- 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * There can be arbitrary padding between the headers and buffers so 518c2ecf20Sopenharmony_ci * you should always use the appropriate scm_legacy_get_*_buffer() routines 528c2ecf20Sopenharmony_ci * to access the buffers in a safe manner. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistruct scm_legacy_command { 558c2ecf20Sopenharmony_ci __le32 len; 568c2ecf20Sopenharmony_ci __le32 buf_offset; 578c2ecf20Sopenharmony_ci __le32 resp_hdr_offset; 588c2ecf20Sopenharmony_ci __le32 id; 598c2ecf20Sopenharmony_ci __le32 buf[]; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * struct scm_legacy_response - one SCM response buffer 648c2ecf20Sopenharmony_ci * @len: total available memory for response 658c2ecf20Sopenharmony_ci * @buf_offset: start of response data relative to start of scm_legacy_response 668c2ecf20Sopenharmony_ci * @is_complete: indicates if the command has finished processing 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistruct scm_legacy_response { 698c2ecf20Sopenharmony_ci __le32 len; 708c2ecf20Sopenharmony_ci __le32 buf_offset; 718c2ecf20Sopenharmony_ci __le32 is_complete; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/** 758c2ecf20Sopenharmony_ci * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response 768c2ecf20Sopenharmony_ci * @cmd: command 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * Returns a pointer to a response for a command. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic inline struct scm_legacy_response *scm_legacy_command_to_response( 818c2ecf20Sopenharmony_ci const struct scm_legacy_command *cmd) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/** 878c2ecf20Sopenharmony_ci * scm_legacy_get_command_buffer() - Get a pointer to a command buffer 888c2ecf20Sopenharmony_ci * @cmd: command 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Returns a pointer to the command buffer of a command. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic inline void *scm_legacy_get_command_buffer( 938c2ecf20Sopenharmony_ci const struct scm_legacy_command *cmd) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return (void *)cmd->buf; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/** 998c2ecf20Sopenharmony_ci * scm_legacy_get_response_buffer() - Get a pointer to a response buffer 1008c2ecf20Sopenharmony_ci * @rsp: response 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Returns a pointer to a response buffer of a response. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic inline void *scm_legacy_get_response_buffer( 1058c2ecf20Sopenharmony_ci const struct scm_legacy_response *rsp) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return (void *)rsp + le32_to_cpu(rsp->buf_offset); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void __scm_legacy_do(const struct arm_smccc_args *smc, 1118c2ecf20Sopenharmony_ci struct arm_smccc_res *res) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci do { 1148c2ecf20Sopenharmony_ci arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], 1158c2ecf20Sopenharmony_ci smc->args[3], smc->args[4], smc->args[5], 1168c2ecf20Sopenharmony_ci smc->args[6], smc->args[7], res); 1178c2ecf20Sopenharmony_ci } while (res->a0 == QCOM_SCM_INTERRUPTED); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * qcom_scm_call() - Sends a command to the SCM and waits for the command to 1228c2ecf20Sopenharmony_ci * finish processing. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * A note on cache maintenance: 1258c2ecf20Sopenharmony_ci * Note that any buffers that are expected to be accessed by the secure world 1268c2ecf20Sopenharmony_ci * must be flushed before invoking qcom_scm_call and invalidated in the cache 1278c2ecf20Sopenharmony_ci * immediately after qcom_scm_call returns. Cache maintenance on the command 1288c2ecf20Sopenharmony_ci * and response buffers is taken care of by qcom_scm_call; however, callers are 1298c2ecf20Sopenharmony_ci * responsible for any other cached buffers passed over to the secure world. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ciint scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, 1328c2ecf20Sopenharmony_ci struct qcom_scm_res *res) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u8 arglen = desc->arginfo & 0xf; 1358c2ecf20Sopenharmony_ci int ret = 0, context_id; 1368c2ecf20Sopenharmony_ci unsigned int i; 1378c2ecf20Sopenharmony_ci struct scm_legacy_command *cmd; 1388c2ecf20Sopenharmony_ci struct scm_legacy_response *rsp; 1398c2ecf20Sopenharmony_ci struct arm_smccc_args smc = {0}; 1408c2ecf20Sopenharmony_ci struct arm_smccc_res smc_res; 1418c2ecf20Sopenharmony_ci const size_t cmd_len = arglen * sizeof(__le32); 1428c2ecf20Sopenharmony_ci const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); 1438c2ecf20Sopenharmony_ci size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; 1448c2ecf20Sopenharmony_ci dma_addr_t cmd_phys; 1458c2ecf20Sopenharmony_ci __le32 *arg_buf; 1468c2ecf20Sopenharmony_ci const __le32 *res_buf; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); 1498c2ecf20Sopenharmony_ci if (!cmd) 1508c2ecf20Sopenharmony_ci return -ENOMEM; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci cmd->len = cpu_to_le32(alloc_len); 1538c2ecf20Sopenharmony_ci cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); 1548c2ecf20Sopenharmony_ci cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); 1558c2ecf20Sopenharmony_ci cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci arg_buf = scm_legacy_get_command_buffer(cmd); 1588c2ecf20Sopenharmony_ci for (i = 0; i < arglen; i++) 1598c2ecf20Sopenharmony_ci arg_buf[i] = cpu_to_le32(desc->args[i]); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci rsp = scm_legacy_command_to_response(cmd); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); 1648c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, cmd_phys)) { 1658c2ecf20Sopenharmony_ci kfree(cmd); 1668c2ecf20Sopenharmony_ci return -ENOMEM; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci smc.args[0] = 1; 1708c2ecf20Sopenharmony_ci smc.args[1] = (unsigned long)&context_id; 1718c2ecf20Sopenharmony_ci smc.args[2] = cmd_phys; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mutex_lock(&qcom_scm_lock); 1748c2ecf20Sopenharmony_ci __scm_legacy_do(&smc, &smc_res); 1758c2ecf20Sopenharmony_ci if (smc_res.a0) 1768c2ecf20Sopenharmony_ci ret = qcom_scm_remap_error(smc_res.a0); 1778c2ecf20Sopenharmony_ci mutex_unlock(&qcom_scm_lock); 1788c2ecf20Sopenharmony_ci if (ret) 1798c2ecf20Sopenharmony_ci goto out; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci do { 1828c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, 1838c2ecf20Sopenharmony_ci sizeof(*rsp), DMA_FROM_DEVICE); 1848c2ecf20Sopenharmony_ci } while (!rsp->is_complete); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + 1878c2ecf20Sopenharmony_ci le32_to_cpu(rsp->buf_offset), 1888c2ecf20Sopenharmony_ci resp_len, DMA_FROM_DEVICE); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (res) { 1918c2ecf20Sopenharmony_ci res_buf = scm_legacy_get_response_buffer(rsp); 1928c2ecf20Sopenharmony_ci for (i = 0; i < MAX_QCOM_SCM_RETS; i++) 1938c2ecf20Sopenharmony_ci res->result[i] = le32_to_cpu(res_buf[i]); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ciout: 1968c2ecf20Sopenharmony_ci dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); 1978c2ecf20Sopenharmony_ci kfree(cmd); 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 2028c2ecf20Sopenharmony_ci#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 2038c2ecf20Sopenharmony_ci#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) 2048c2ecf20Sopenharmony_ci#define SCM_LEGACY_MASK_IRQS BIT(5) 2058c2ecf20Sopenharmony_ci#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ 2068c2ecf20Sopenharmony_ci ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ 2078c2ecf20Sopenharmony_ci SCM_LEGACY_CLASS_REGISTER | \ 2088c2ecf20Sopenharmony_ci SCM_LEGACY_MASK_IRQS | \ 2098c2ecf20Sopenharmony_ci (n & 0xf)) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments 2138c2ecf20Sopenharmony_ci * and 3 return values 2148c2ecf20Sopenharmony_ci * @desc: SCM call descriptor containing arguments 2158c2ecf20Sopenharmony_ci * @res: SCM call return values 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * This shall only be used with commands that are guaranteed to be 2188c2ecf20Sopenharmony_ci * uninterruptable, atomic and SMP safe. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ciint scm_legacy_call_atomic(struct device *unused, 2218c2ecf20Sopenharmony_ci const struct qcom_scm_desc *desc, 2228c2ecf20Sopenharmony_ci struct qcom_scm_res *res) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci int context_id; 2258c2ecf20Sopenharmony_ci struct arm_smccc_res smc_res; 2268c2ecf20Sopenharmony_ci size_t arglen = desc->arginfo & 0xf; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), 2318c2ecf20Sopenharmony_ci (unsigned long)&context_id, 2328c2ecf20Sopenharmony_ci desc->args[0], desc->args[1], desc->args[2], 2338c2ecf20Sopenharmony_ci desc->args[3], desc->args[4], 0, &smc_res); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (res) { 2368c2ecf20Sopenharmony_ci res->result[0] = smc_res.a1; 2378c2ecf20Sopenharmony_ci res->result[1] = smc_res.a2; 2388c2ecf20Sopenharmony_ci res->result[2] = smc_res.a3; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return smc_res.a0; 2428c2ecf20Sopenharmony_ci} 243