162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/mutex.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/firmware/qcom/qcom_scm.h> 1362306a36Sopenharmony_ci#include <linux/arm-smccc.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "qcom_scm.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic DEFINE_MUTEX(qcom_scm_lock); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * struct arm_smccc_args 2362306a36Sopenharmony_ci * @args: The array of values used in registers in smc instruction 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_cistruct arm_smccc_args { 2662306a36Sopenharmony_ci unsigned long args[8]; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * struct scm_legacy_command - one SCM command buffer 3262306a36Sopenharmony_ci * @len: total available memory for command and response 3362306a36Sopenharmony_ci * @buf_offset: start of command buffer 3462306a36Sopenharmony_ci * @resp_hdr_offset: start of response buffer 3562306a36Sopenharmony_ci * @id: command to be executed 3662306a36Sopenharmony_ci * @buf: buffer returned from scm_legacy_get_command_buffer() 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * An SCM command is laid out in memory as follows: 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * ------------------- <--- struct scm_legacy_command 4162306a36Sopenharmony_ci * | command header | 4262306a36Sopenharmony_ci * ------------------- <--- scm_legacy_get_command_buffer() 4362306a36Sopenharmony_ci * | command buffer | 4462306a36Sopenharmony_ci * ------------------- <--- struct scm_legacy_response and 4562306a36Sopenharmony_ci * | response header | scm_legacy_command_to_response() 4662306a36Sopenharmony_ci * ------------------- <--- scm_legacy_get_response_buffer() 4762306a36Sopenharmony_ci * | response buffer | 4862306a36Sopenharmony_ci * ------------------- 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * There can be arbitrary padding between the headers and buffers so 5162306a36Sopenharmony_ci * you should always use the appropriate scm_legacy_get_*_buffer() routines 5262306a36Sopenharmony_ci * to access the buffers in a safe manner. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct scm_legacy_command { 5562306a36Sopenharmony_ci __le32 len; 5662306a36Sopenharmony_ci __le32 buf_offset; 5762306a36Sopenharmony_ci __le32 resp_hdr_offset; 5862306a36Sopenharmony_ci __le32 id; 5962306a36Sopenharmony_ci __le32 buf[]; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * struct scm_legacy_response - one SCM response buffer 6462306a36Sopenharmony_ci * @len: total available memory for response 6562306a36Sopenharmony_ci * @buf_offset: start of response data relative to start of scm_legacy_response 6662306a36Sopenharmony_ci * @is_complete: indicates if the command has finished processing 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistruct scm_legacy_response { 6962306a36Sopenharmony_ci __le32 len; 7062306a36Sopenharmony_ci __le32 buf_offset; 7162306a36Sopenharmony_ci __le32 is_complete; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response 7662306a36Sopenharmony_ci * @cmd: command 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Returns a pointer to a response for a command. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic inline struct scm_legacy_response *scm_legacy_command_to_response( 8162306a36Sopenharmony_ci const struct scm_legacy_command *cmd) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * scm_legacy_get_command_buffer() - Get a pointer to a command buffer 8862306a36Sopenharmony_ci * @cmd: command 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Returns a pointer to the command buffer of a command. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic inline void *scm_legacy_get_command_buffer( 9362306a36Sopenharmony_ci const struct scm_legacy_command *cmd) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return (void *)cmd->buf; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/** 9962306a36Sopenharmony_ci * scm_legacy_get_response_buffer() - Get a pointer to a response buffer 10062306a36Sopenharmony_ci * @rsp: response 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Returns a pointer to a response buffer of a response. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic inline void *scm_legacy_get_response_buffer( 10562306a36Sopenharmony_ci const struct scm_legacy_response *rsp) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return (void *)rsp + le32_to_cpu(rsp->buf_offset); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void __scm_legacy_do(const struct arm_smccc_args *smc, 11162306a36Sopenharmony_ci struct arm_smccc_res *res) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci do { 11462306a36Sopenharmony_ci arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], 11562306a36Sopenharmony_ci smc->args[3], smc->args[4], smc->args[5], 11662306a36Sopenharmony_ci smc->args[6], smc->args[7], res); 11762306a36Sopenharmony_ci } while (res->a0 == QCOM_SCM_INTERRUPTED); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * scm_legacy_call() - Sends a command to the SCM and waits for the command to 12262306a36Sopenharmony_ci * finish processing. 12362306a36Sopenharmony_ci * @dev: device 12462306a36Sopenharmony_ci * @desc: descriptor structure containing arguments and return values 12562306a36Sopenharmony_ci * @res: results from SMC call 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * A note on cache maintenance: 12862306a36Sopenharmony_ci * Note that any buffers that are expected to be accessed by the secure world 12962306a36Sopenharmony_ci * must be flushed before invoking qcom_scm_call and invalidated in the cache 13062306a36Sopenharmony_ci * immediately after qcom_scm_call returns. Cache maintenance on the command 13162306a36Sopenharmony_ci * and response buffers is taken care of by qcom_scm_call; however, callers are 13262306a36Sopenharmony_ci * responsible for any other cached buffers passed over to the secure world. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ciint scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, 13562306a36Sopenharmony_ci struct qcom_scm_res *res) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u8 arglen = desc->arginfo & 0xf; 13862306a36Sopenharmony_ci int ret = 0, context_id; 13962306a36Sopenharmony_ci unsigned int i; 14062306a36Sopenharmony_ci struct scm_legacy_command *cmd; 14162306a36Sopenharmony_ci struct scm_legacy_response *rsp; 14262306a36Sopenharmony_ci struct arm_smccc_args smc = {0}; 14362306a36Sopenharmony_ci struct arm_smccc_res smc_res; 14462306a36Sopenharmony_ci const size_t cmd_len = arglen * sizeof(__le32); 14562306a36Sopenharmony_ci const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); 14662306a36Sopenharmony_ci size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; 14762306a36Sopenharmony_ci dma_addr_t cmd_phys; 14862306a36Sopenharmony_ci __le32 *arg_buf; 14962306a36Sopenharmony_ci const __le32 *res_buf; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); 15262306a36Sopenharmony_ci if (!cmd) 15362306a36Sopenharmony_ci return -ENOMEM; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci cmd->len = cpu_to_le32(alloc_len); 15662306a36Sopenharmony_ci cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); 15762306a36Sopenharmony_ci cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); 15862306a36Sopenharmony_ci cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci arg_buf = scm_legacy_get_command_buffer(cmd); 16162306a36Sopenharmony_ci for (i = 0; i < arglen; i++) 16262306a36Sopenharmony_ci arg_buf[i] = cpu_to_le32(desc->args[i]); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rsp = scm_legacy_command_to_response(cmd); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); 16762306a36Sopenharmony_ci if (dma_mapping_error(dev, cmd_phys)) { 16862306a36Sopenharmony_ci kfree(cmd); 16962306a36Sopenharmony_ci return -ENOMEM; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci smc.args[0] = 1; 17362306a36Sopenharmony_ci smc.args[1] = (unsigned long)&context_id; 17462306a36Sopenharmony_ci smc.args[2] = cmd_phys; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mutex_lock(&qcom_scm_lock); 17762306a36Sopenharmony_ci __scm_legacy_do(&smc, &smc_res); 17862306a36Sopenharmony_ci if (smc_res.a0) 17962306a36Sopenharmony_ci ret = qcom_scm_remap_error(smc_res.a0); 18062306a36Sopenharmony_ci mutex_unlock(&qcom_scm_lock); 18162306a36Sopenharmony_ci if (ret) 18262306a36Sopenharmony_ci goto out; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci do { 18562306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, 18662306a36Sopenharmony_ci sizeof(*rsp), DMA_FROM_DEVICE); 18762306a36Sopenharmony_ci } while (!rsp->is_complete); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + 19062306a36Sopenharmony_ci le32_to_cpu(rsp->buf_offset), 19162306a36Sopenharmony_ci resp_len, DMA_FROM_DEVICE); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (res) { 19462306a36Sopenharmony_ci res_buf = scm_legacy_get_response_buffer(rsp); 19562306a36Sopenharmony_ci for (i = 0; i < MAX_QCOM_SCM_RETS; i++) 19662306a36Sopenharmony_ci res->result[i] = le32_to_cpu(res_buf[i]); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ciout: 19962306a36Sopenharmony_ci dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); 20062306a36Sopenharmony_ci kfree(cmd); 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 20562306a36Sopenharmony_ci#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 20662306a36Sopenharmony_ci#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) 20762306a36Sopenharmony_ci#define SCM_LEGACY_MASK_IRQS BIT(5) 20862306a36Sopenharmony_ci#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ 20962306a36Sopenharmony_ci ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ 21062306a36Sopenharmony_ci SCM_LEGACY_CLASS_REGISTER | \ 21162306a36Sopenharmony_ci SCM_LEGACY_MASK_IRQS | \ 21262306a36Sopenharmony_ci (n & 0xf)) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments 21662306a36Sopenharmony_ci * and 3 return values 21762306a36Sopenharmony_ci * @unused: device, legacy argument, not used, can be NULL 21862306a36Sopenharmony_ci * @desc: SCM call descriptor containing arguments 21962306a36Sopenharmony_ci * @res: SCM call return values 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * This shall only be used with commands that are guaranteed to be 22262306a36Sopenharmony_ci * uninterruptable, atomic and SMP safe. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ciint scm_legacy_call_atomic(struct device *unused, 22562306a36Sopenharmony_ci const struct qcom_scm_desc *desc, 22662306a36Sopenharmony_ci struct qcom_scm_res *res) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci int context_id; 22962306a36Sopenharmony_ci struct arm_smccc_res smc_res; 23062306a36Sopenharmony_ci size_t arglen = desc->arginfo & 0xf; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), 23562306a36Sopenharmony_ci (unsigned long)&context_id, 23662306a36Sopenharmony_ci desc->args[0], desc->args[1], desc->args[2], 23762306a36Sopenharmony_ci desc->args[3], desc->args[4], 0, &smc_res); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (res) { 24062306a36Sopenharmony_ci res->result[0] = smc_res.a1; 24162306a36Sopenharmony_ci res->result[1] = smc_res.a2; 24262306a36Sopenharmony_ci res->result[2] = smc_res.a3; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return smc_res.a0; 24662306a36Sopenharmony_ci} 247