Message ID | 1572917256-24205-18-git-send-email-eberman@codeaurora.org (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Restructure, improve target support for qcom_scm driver | expand |
On Mon 04 Nov 17:27 PST 2019, Elliot Berman wrote: > - Rename qcom_scm-64.c to qcom_scm-smc as it supports SMC-based calling conventions > for 32 and 64-bit targets > - Remove qcom_scm-32.c as it has been superceded by qcom_scm-smc.c > - Update Kconfig to select QCOM_SCM_TRANSPORT_SMC on ARM and ARM64 > targets > I really like the end result of this series, it's much cleaner than the previous 32 vs 64 split, need for wrappers and risk of introducing build errors when adding new functions. I do think we you can improve this even more by inlining the __functions from qcom_scm-smc.c into qcom_scm.c directly and provide the bottom parts in separate files; qcom_scm-legacy.c and qcom_scm-smccc.c Right now it doesn't look bad, but if qcom_scm.c grows too big I think we can, as discussed, split it up in per-service implementation (still link it all together as one module). > Signed-off-by: Elliot Berman <eberman@codeaurora.org> > --- > drivers/firmware/Kconfig | 18 +- > drivers/firmware/Makefile | 4 +- > drivers/firmware/qcom_scm-32.c | 660 ---------------------------- > drivers/firmware/qcom_scm-64.c | 949 ---------------------------------------- > drivers/firmware/qcom_scm-smc.c | 949 ++++++++++++++++++++++++++++++++++++++++ Pass -M to format-patch and it will describe renames. > 5 files changed, 961 insertions(+), 1619 deletions(-) > delete mode 100644 drivers/firmware/qcom_scm-32.c > delete mode 100644 drivers/firmware/qcom_scm-64.c > create mode 100644 drivers/firmware/qcom_scm-smc.c > > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > index e40a77b..ad477ca 100644 > --- a/drivers/firmware/Kconfig > +++ b/drivers/firmware/Kconfig > @@ -236,16 +236,20 @@ config INTEL_STRATIX10_RSU > > config QCOM_SCM > bool > - depends on ARM || ARM64 > + depends on QCOM_SCM_TRANSPORT_SMC > select RESET_CONTROLLER > + help > + This option enables support for communicating with > + Qualcomm secure worlds. > > -config QCOM_SCM_32 > - def_bool y > - depends on QCOM_SCM && ARM > - > -config QCOM_SCM_64 > +config QCOM_SCM_TRANSPORT_SMC No need for a separate option, rely on QCOM_SCM and build everything into one module. Regards, Bjorn > def_bool y > - depends on QCOM_SCM && ARM64 > + depends on ARM || ARM64 > + help > + This option enables support for communicating with > + Qualcomm secure worlds via the SMC instruction, using > + either ARM SMCCC or legacy calling convention by querying > + the Qualcomm secure world. > > config QCOM_SCM_DOWNLOAD_MODE_DEFAULT > bool "Qualcomm download mode enabled by default" > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > index 3fcb919..fc250f0 100644 > --- a/drivers/firmware/Makefile > +++ b/drivers/firmware/Makefile > @@ -18,9 +18,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o > obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o > obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o > obj-$(CONFIG_QCOM_SCM) += qcom_scm.o > -obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o > -obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o > -CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a > +obj-$(CONFIG_QCOM_SCM_TRANSPORT_SMC) += qcom_scm-smc.o > obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o > obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o > obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o > diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c > deleted file mode 100644 > index 3325c21..0000000 > --- a/drivers/firmware/qcom_scm-32.c > +++ /dev/null > @@ -1,660 +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 <linux/slab.h> > -#include <linux/io.h> > -#include <linux/module.h> > -#include <linux/mutex.h> > -#include <linux/errno.h> > -#include <linux/err.h> > -#include <linux/qcom_scm.h> > -#include <linux/arm-smccc.h> > -#include <linux/dma-mapping.h> > - > -#include "qcom_scm.h" > - > -#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 > - > -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 > - > -struct qcom_scm_entry { > - int flag; > - void *entry; > -}; > - > -static struct qcom_scm_entry qcom_scm_wb[] = { > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, > -}; > - > -static DEFINE_MUTEX(qcom_scm_lock); > - > -#define MAX_QCOM_SCM_ARGS 10 > -#define MAX_QCOM_SCM_RETS 3 > - > -enum qcom_scm_arg_types { > - QCOM_SCM_VAL, > - QCOM_SCM_RO, > - QCOM_SCM_RW, > - QCOM_SCM_BUFVAL, > -}; > - > -#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ > - (((a) & 0x3) << 4) | \ > - (((b) & 0x3) << 6) | \ > - (((c) & 0x3) << 8) | \ > - (((d) & 0x3) << 10) | \ > - (((e) & 0x3) << 12) | \ > - (((f) & 0x3) << 14) | \ > - (((g) & 0x3) << 16) | \ > - (((h) & 0x3) << 18) | \ > - (((i) & 0x3) << 20) | \ > - (((j) & 0x3) << 22) | \ > - ((num) & 0xf)) > - > -#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) > - > -/** > - * struct qcom_scm_desc > - * @arginfo: Metadata describing the arguments in args[] > - * @args: The array of arguments for the secure syscall > - * @res: The values returned by the secure syscall > - */ > -struct qcom_scm_desc { > - u32 svc; > - u32 cmd; > - u32 arginfo; > - u64 args[MAX_QCOM_SCM_ARGS]; > - u64 res[MAX_QCOM_SCM_RETS]; > - u32 owner; > -}; > -struct arm_smccc_args { > - unsigned long a[8]; > -}; > - > -#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) > - > -/** > - * struct 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 legacy_get_command_buffer() > - * > - * An SCM command is laid out in memory as follows: > - * > - * ------------------- <--- struct legacy_command > - * | command header | > - * ------------------- <--- legacy_get_command_buffer() > - * | command buffer | > - * ------------------- <--- struct legacy_response and > - * | response header | legacy_command_to_response() > - * ------------------- <--- legacy_get_response_buffer() > - * | response buffer | > - * ------------------- > - * > - * There can be arbitrary padding between the headers and buffers so > - * you should always use the appropriate qcom_scm_get_*_buffer() routines > - * to access the buffers in a safe manner. > - */ > -struct legacy_command { > - __le32 len; > - __le32 buf_offset; > - __le32 resp_hdr_offset; > - __le32 id; > - __le32 buf[0]; > -}; > - > -/** > - * struct legacy_response - one SCM response buffer > - * @len: total available memory for response > - * @buf_offset: start of response data relative to start of legacy_response > - * @is_complete: indicates if the command has finished processing > - */ > -struct legacy_response { > - __le32 len; > - __le32 buf_offset; > - __le32 is_complete; > -}; > - > -/** > - * legacy_command_to_response() - Get a pointer to a legacy_response > - * @cmd: command > - * > - * Returns a pointer to a response for a command. > - */ > -static inline struct legacy_response *legacy_command_to_response( > - const struct legacy_command *cmd) > -{ > - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); > -} > - > -/** > - * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) > -{ > - return (void *)cmd->buf; > -} > - > -/** > - * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) > -{ > - return (void *)rsp + le32_to_cpu(rsp->buf_offset); > -} > - > -static void __qcom_scm_call_do(const struct arm_smccc_args *smc, > - struct arm_smccc_res *res) > -{ > - do { > - arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], > - smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); > - } while (res->a0 == QCOM_SCM_INTERRUPTED); > -} > - > -/** > - * qcom_scm_call() - Send an SCM command > - * @dev: struct device > - * @svc_id: service identifier > - * @cmd_id: command identifier > - * @cmd_buf: command buffer > - * @cmd_len: length of the command buffer > - * @resp_buf: response buffer > - * @resp_len: length of the response buffer > - * > - * 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. > - */ > -static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) > -{ > - int arglen = desc->arginfo & 0xf; > - int ret = 0, context_id; > - size_t i; > - struct legacy_command *cmd; > - struct legacy_response *rsp; > - struct arm_smccc_args smc = {0}; > - struct arm_smccc_res 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; > - __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); > - > - arg_buf = legacy_get_command_buffer(cmd); > - for (i = 0; i < arglen; i++) > - arg_buf[i] = cpu_to_le32(desc->args[i]); > - > - rsp = 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.a[0] = 1; > - smc.a[1] = (unsigned long)&context_id; > - smc.a[2] = cmd_phys; > - > - mutex_lock(&qcom_scm_lock); > - __qcom_scm_call_do(&smc, &res); > - if (res.a0 < 0) > - ret = qcom_scm_remap_error(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); > - > - res_buf = legacy_get_response_buffer(rsp); > - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) > - desc->res[i] = le32_to_cpu(res_buf[i]); > -out: > - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); > - kfree(cmd); > - return ret; > -} > - > -#define LEGACY_ATOMIC_N_REG_ARGS 5 > -#define LEGACY_ATOMIC_FIRST_REG_IDX 2 > -#define LEGACY_CLASS_REGISTER (0x2 << 8) > -#define LEGACY_MASK_IRQS BIT(5) > -#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ > - LEGACY_CLASS_REGISTER | \ > - LEGACY_MASK_IRQS | \ > - (n & 0xf)) > - > -/** > - * qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments > - * and 3 return values > - * > - * This shall only be used with commands that are guaranteed to be > - * uninterruptable, atomic and SMP safe. > - */ > -static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) > -{ > - int context_id; > - struct arm_smccc_args smc = {0}; > - struct arm_smccc_res res; > - size_t i, arglen = desc->arginfo & 0xf; > - > - BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); > - > - smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); > - smc.a[1] = (unsigned long)&context_id; > - > - for (i = 0; i < arglen; i++) > - smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; > - > - arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], > - smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); > - > - desc->res[0] = res.a1; > - desc->res[1] = res.a2; > - desc->res[2] = res.a3; > - > - return res.a0; > -} > - > -/** > - * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus > - * @entry: Entry point function for the cpus > - * @cpus: The cpumask of cpus that will use the entry point > - * > - * Set the cold boot address of the cpus. Any cpu outside the supported > - * range would be removed from the cpu present mask. > - */ > -int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, > - const cpumask_t *cpus) > -{ > - int flags = 0; > - int cpu; > - int scm_cb_flags[] = { > - QCOM_SCM_FLAG_COLDBOOT_CPU0, > - QCOM_SCM_FLAG_COLDBOOT_CPU1, > - QCOM_SCM_FLAG_COLDBOOT_CPU2, > - QCOM_SCM_FLAG_COLDBOOT_CPU3, > - }; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_ADDR, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - if (!cpus || (cpus && cpumask_empty(cpus))) > - return -EINVAL; > - > - for_each_cpu(cpu, cpus) { > - if (cpu < ARRAY_SIZE(scm_cb_flags)) > - flags |= scm_cb_flags[cpu]; > - else > - set_cpu_present(cpu, false); > - } > - > - desc.args[0] = flags; > - desc.args[1] = virt_to_phys(entry); > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -/** > - * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus > - * @entry: Entry point function for the cpus > - * @cpus: The cpumask of cpus that will use the entry point > - * > - * Set the Linux entry point for the SCM to transfer control to when coming > - * out of a power down. CPU power down may be executed on cpuidle or hotplug. > - */ > -int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, > - const cpumask_t *cpus) > -{ > - int ret; > - int flags = 0; > - int cpu; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_ADDR, > - .arginfo = QCOM_SCM_ARGS(2), > - }; > - > - /* > - * Reassign only if we are switching from hotplug entry point > - * to cpuidle entry point or vice versa. > - */ > - for_each_cpu(cpu, cpus) { > - if (entry == qcom_scm_wb[cpu].entry) > - continue; > - flags |= qcom_scm_wb[cpu].flag; > - } > - > - /* No change in entry function */ > - if (!flags) > - return 0; > - > - desc.args[0] = flags; > - desc.args[1] = virt_to_phys(entry); > - ret = qcom_scm_call(dev, &desc); > - if (!ret) { > - for_each_cpu(cpu, cpus) > - qcom_scm_wb[cpu].entry = entry; > - } > - > - return ret; > -} > - > -/** > - * qcom_scm_cpu_power_down() - Power down the cpu > - * @flags - Flags to flush cache > - * > - * This is an end point to power down cpu. If there was a pending interrupt, > - * the control would return from this function, otherwise, the cpu jumps to the > - * warm boot entry point set for this cpu upon reset. > - */ > -void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_TERMINATE_PC, > - .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, > - .arginfo = QCOM_SCM_ARGS(1), > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - qcom_scm_call_atomic(dev, &desc); > -} > - > -int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = state; > - desc.args[1] = id; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_set_dload_mode(struct device *dev, bool enable) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; > - desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? false : !!desc.res[0]; > -} > - > -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, > - dma_addr_t metadata_phys) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.args[1] = metadata_phys; > - desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, > - phys_addr_t addr, phys_addr_t size) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.args[1] = addr; > - desc.args[2] = size; > - desc.arginfo = QCOM_SCM_ARGS(3); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = peripheral; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = reset; > - desc.args[1] = 0; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, > - unsigned int *val) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_IO, > - .cmd = QCOM_SCM_IO_READ, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = addr; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call_atomic(dev, &desc); > - if (ret >= 0) > - *val = desc.res[0]; > - > - return ret < 0 ? ret : 0; > -} > - > -int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_IO, > - .cmd = QCOM_SCM_IO_WRITE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = addr; > - desc.args[1] = val; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -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] = (svc_id << 10) | cmd_id, > - .arginfo = QCOM_SCM_ARGS(1), > - }; > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, > - u32 spare) > -{ > - return -ENODEV; > -} > - > -int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, > - size_t *size) > -{ > - return -ENODEV; > -} > - > -int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, > - u32 spare) > -{ > - return -ENODEV; > -} > - > -int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, > - size_t mem_sz, phys_addr_t src, size_t src_sz, > - phys_addr_t dest, size_t dest_sz) > -{ > - return -ENODEV; > -} > - > -int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, > - u32 req_cnt, u32 *resp) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_HDCP, > - .cmd = QCOM_SCM_HDCP_INVOKE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) > - return -ERANGE; > - > - desc.args[0] = req[0].addr; > - desc.args[1] = req[0].val; > - desc.args[2] = req[1].addr; > - desc.args[3] = req[1].val; > - desc.args[4] = req[2].addr; > - desc.args[5] = req[2].val; > - desc.args[6] = req[3].addr; > - desc.args[7] = req[3].val; > - desc.args[8] = req[4].addr; > - desc.args[9] = req[4].val; > - desc.arginfo = QCOM_SCM_ARGS(req_cnt * 2); > - > - ret = qcom_scm_call(dev, &desc); > - *resp = desc.res[0]; > - > - return ret; > -} > - > -int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool enable) > -{ > - return -ENODEV; > -} > - > -void __qcom_scm_init(void) > -{ > -} > diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c > deleted file mode 100644 > index 355d9d7..0000000 > --- a/drivers/firmware/qcom_scm-64.c > +++ /dev/null > @@ -1,949 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0-only > -/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. > - */ > - > -#include <linux/io.h> > -#include <linux/errno.h> > -#include <linux/delay.h> > -#include <linux/mutex.h> > -#include <linux/slab.h> > -#include <linux/types.h> > -#include <linux/qcom_scm.h> > -#include <linux/arm-smccc.h> > -#include <linux/dma-mapping.h> > - > -#include "qcom_scm.h" > - > -#define MAX_QCOM_SCM_ARGS 10 > -#define MAX_QCOM_SCM_RETS 3 > - > -enum qcom_scm_arg_types { > - QCOM_SCM_VAL, > - QCOM_SCM_RO, > - QCOM_SCM_RW, > - QCOM_SCM_BUFVAL, > -}; > - > -#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ > - (((a) & 0x3) << 4) | \ > - (((b) & 0x3) << 6) | \ > - (((c) & 0x3) << 8) | \ > - (((d) & 0x3) << 10) | \ > - (((e) & 0x3) << 12) | \ > - (((f) & 0x3) << 14) | \ > - (((g) & 0x3) << 16) | \ > - (((h) & 0x3) << 18) | \ > - (((i) & 0x3) << 20) | \ > - (((j) & 0x3) << 22) | \ > - ((num) & 0xf)) > - > -#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) > - > -/** > - * struct qcom_scm_desc > - * @arginfo: Metadata describing the arguments in args[] > - * @args: The array of arguments for the secure syscall > - * @res: The values returned by the secure syscall > - */ > -struct qcom_scm_desc { > - u32 svc; > - u32 cmd; > - u32 arginfo; > - u64 args[MAX_QCOM_SCM_ARGS]; > - u64 res[MAX_QCOM_SCM_RETS]; > - u32 owner; > -}; > - > -struct arm_smccc_args { > - unsigned long a[8]; > -}; > - > -enum qcom_smc_convention { > - SMC_CONVENTION_UNKNOWN, > - SMC_CONVENTION_LEGACY, > - SMC_CONVENTION_ARM_32, > - SMC_CONVENTION_ARM_64, > -}; > - > -static enum qcom_smc_convention qcom_smc_convention = SMC_CONVENTION_UNKNOWN; > -static DEFINE_MUTEX(qcom_scm_lock); > - > -#define QCOM_SCM_EBUSY_WAIT_MS 30 > -#define QCOM_SCM_EBUSY_MAX_RETRY 20 > - > -#define SMCCC_FUNCNUM(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) > -#define SMCCC_N_REG_ARGS 4 > -#define SMCCC_FIRST_REG_IDX 2 > -#define SMCCC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SMCCC_N_REG_ARGS + 1) > -#define SMCCC_LAST_REG_IDX (SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1) > - > -static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc, > - struct arm_smccc_res *res) > -{ > - unsigned long a0 = smc->a[0]; > - struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; > - > - quirk.state.a6 = 0; > - > - do { > - arm_smccc_smc_quirk(a0, smc->a[1], smc->a[2], smc->a[3], > - smc->a[4], smc->a[5], quirk.state.a6, > - smc->a[7], res, &quirk); > - > - if (res->a0 == QCOM_SCM_INTERRUPTED) > - a0 = res->a0; > - > - } while (res->a0 == QCOM_SCM_INTERRUPTED); > -} > - > -static int qcom_scm_call_smccc(struct device *dev, > - struct qcom_scm_desc *desc, bool atomic) > -{ > - int arglen = desc->arginfo & 0xf; > - int i; > - dma_addr_t args_phys = 0; > - void *args_virt = NULL; > - size_t alloc_len; > - gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; > - struct arm_smccc_res res; > - struct arm_smccc_args smc = {0}; > - > - smc.a[0] = ARM_SMCCC_CALL_VAL( > - atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL, > - (qcom_smc_convention == SMC_CONVENTION_ARM_64) ? > - ARM_SMCCC_SMC_64 : > - ARM_SMCCC_SMC_32, > - desc->owner, > - SMCCC_FUNCNUM(desc->svc, desc->cmd)); > - smc.a[1] = desc->arginfo; > - for (i = 0; i < SMCCC_N_REG_ARGS; i++) > - smc.a[i + SMCCC_FIRST_REG_IDX] = desc->args[i]; > - > - if (unlikely(arglen > SMCCC_N_REG_ARGS)) { > - alloc_len = SMCCC_N_EXT_ARGS * sizeof(u64); > - args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); > - > - if (!args_virt) > - return -ENOMEM; > - > - if (qcom_smc_convention == SMC_CONVENTION_ARM_32) { > - __le32 *args = args_virt; > - > - for (i = 0; i < SMCCC_N_EXT_ARGS; i++) > - args[i] = cpu_to_le32(desc->args[i + > - SMCCC_N_REG_ARGS - 1]); > - } else { > - __le64 *args = args_virt; > - > - for (i = 0; i < SMCCC_N_EXT_ARGS; i++) > - args[i] = cpu_to_le64(desc->args[i + > - SMCCC_N_REG_ARGS - 1]); > - } > - > - args_phys = dma_map_single(dev, args_virt, alloc_len, > - DMA_TO_DEVICE); > - > - if (dma_mapping_error(dev, args_phys)) { > - kfree(args_virt); > - return -ENOMEM; > - } > - > - smc.a[SMCCC_LAST_REG_IDX] = args_phys; > - } > - > - if (atomic) { > - __qcom_scm_call_do_quirk(&smc, &res); > - } else { > - int retry_count = 0; > - > - do { > - mutex_lock(&qcom_scm_lock); > - > - __qcom_scm_call_do_quirk(&smc, &res); > - > - mutex_unlock(&qcom_scm_lock); > - > - if (res.a0 == QCOM_SCM_V2_EBUSY) { > - if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) > - break; > - msleep(QCOM_SCM_EBUSY_WAIT_MS); > - } > - } while (res.a0 == QCOM_SCM_V2_EBUSY); > - } > - > - if (args_virt) { > - dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); > - kfree(args_virt); > - } > - > - desc->res[0] = res.a1; > - desc->res[1] = res.a2; > - desc->res[2] = res.a3; > - > - if (res.a0 < 0) > - return qcom_scm_remap_error(res.a0); > - > - return 0; > -} > - > -#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) > - > -/** > - * struct 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 legacy_get_command_buffer() > - * > - * An SCM command is laid out in memory as follows: > - * > - * ------------------- <--- struct legacy_command > - * | command header | > - * ------------------- <--- legacy_get_command_buffer() > - * | command buffer | > - * ------------------- <--- struct legacy_response and > - * | response header | legacy_command_to_response() > - * ------------------- <--- legacy_get_response_buffer() > - * | response buffer | > - * ------------------- > - * > - * There can be arbitrary padding between the headers and buffers so > - * you should always use the appropriate qcom_scm_get_*_buffer() routines > - * to access the buffers in a safe manner. > - */ > -struct legacy_command { > - __le32 len; > - __le32 buf_offset; > - __le32 resp_hdr_offset; > - __le32 id; > - __le32 buf[0]; > -}; > - > -/** > - * struct legacy_response - one SCM response buffer > - * @len: total available memory for response > - * @buf_offset: start of response data relative to start of legacy_response > - * @is_complete: indicates if the command has finished processing > - */ > -struct legacy_response { > - __le32 len; > - __le32 buf_offset; > - __le32 is_complete; > -}; > - > -/** > - * legacy_command_to_response() - Get a pointer to a legacy_response > - * @cmd: command > - * > - * Returns a pointer to a response for a command. > - */ > -static inline struct legacy_response *legacy_command_to_response( > - const struct legacy_command *cmd) > -{ > - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); > -} > - > -/** > - * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) > -{ > - return (void *)cmd->buf; > -} > - > -/** > - * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) > -{ > - return (void *)rsp + le32_to_cpu(rsp->buf_offset); > -} > - > -static void __qcom_scm_call_do(const struct arm_smccc_args *smc, > - struct arm_smccc_res *res) > -{ > - do { > - arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], > - smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); > - } while (res->a0 == QCOM_SCM_INTERRUPTED); > -} > - > -/** > - * qcom_scm_call_legacy() - Send an SCM command > - * @dev: struct device > - * @svc_id: service identifier > - * @cmd_id: command identifier > - * @cmd_buf: command buffer > - * @cmd_len: length of the command buffer > - * @resp_buf: response buffer > - * @resp_len: length of the response buffer > - * > - * 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. > - */ > -static int qcom_scm_call_legacy(struct device *dev, struct qcom_scm_desc *desc) > -{ > - int arglen = desc->arginfo & 0xf; > - int ret = 0, context_id; > - size_t i; > - struct legacy_command *cmd; > - struct legacy_response *rsp; > - struct arm_smccc_args smc = {0}; > - struct arm_smccc_res 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; > - __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); > - > - arg_buf = legacy_get_command_buffer(cmd); > - for (i = 0; i < arglen; i++) > - arg_buf[i] = cpu_to_le32(desc->args[i]); > - > - rsp = 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.a[0] = 1; > - smc.a[1] = (unsigned long)&context_id; > - smc.a[2] = cmd_phys; > - > - mutex_lock(&qcom_scm_lock); > - __qcom_scm_call_do(&smc, &res); > - if (res.a0 < 0) > - ret = qcom_scm_remap_error(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); > - > - res_buf = legacy_get_response_buffer(rsp); > - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) > - desc->res[i] = le32_to_cpu(res_buf[i]); > -out: > - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); > - kfree(cmd); > - return ret; > -} > - > -#define LEGACY_ATOMIC_N_REG_ARGS 5 > -#define LEGACY_ATOMIC_FIRST_REG_IDX 2 > -#define LEGACY_CLASS_REGISTER (0x2 << 8) > -#define LEGACY_MASK_IRQS BIT(5) > -#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ > - LEGACY_CLASS_REGISTER | \ > - LEGACY_MASK_IRQS | \ > - (n & 0xf)) > - > -/** > - * qcom_scm_call_atomic_legacy() - Send an atomic SCM command with up to > - * 5 arguments and 3 return values > - * > - * This shall only be used with commands that are guaranteed to be > - * uninterruptable, atomic and SMP safe. > - */ > -static int qcom_scm_call_atomic_legacy(struct device *dev, > - struct qcom_scm_desc *desc) > -{ > - int context_id; > - struct arm_smccc_args smc = {0}; > - struct arm_smccc_res res; > - size_t i, arglen = desc->arginfo & 0xf; > - > - BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); > - > - smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); > - smc.a[1] = (unsigned long)&context_id; > - > - for (i = 0; i < arglen; i++) > - smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; > - > - arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], > - smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); > - > - desc->res[0] = res.a1; > - desc->res[1] = res.a2; > - desc->res[2] = res.a3; > - > - return res.a0; > -} > - > -/** > - * qcom_scm_call() - Invoke a syscall in the secure world > - * @dev: device > - * @svc_id: service identifier > - * @cmd_id: command identifier > - * @desc: Descriptor structure containing arguments and return values > - * > - * Sends a command to the SCM and waits for the command to finish processing. > - * This should *only* be called in pre-emptible context. > - */ > -static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) > -{ > - might_sleep(); > - switch (qcom_smc_convention) { > - case SMC_CONVENTION_ARM_32: > - case SMC_CONVENTION_ARM_64: > - return qcom_scm_call_smccc(dev, desc, false); > - case SMC_CONVENTION_LEGACY: > - return qcom_scm_call_legacy(dev, desc); > - default: > - pr_err("Unknown current SCM calling convention.\n"); > - return -EINVAL; > - } > -} > - > -/** > - * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() > - * @dev: device > - * @svc_id: service identifier > - * @cmd_id: command identifier > - * @desc: Descriptor structure containing arguments and return values > - * @res: Structure containing results from SMC/HVC call > - * > - * Sends a command to the SCM and waits for the command to finish processing. > - * This can be called in atomic context. > - */ > -static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) > -{ > - switch (qcom_smc_convention) { > - case SMC_CONVENTION_ARM_32: > - case SMC_CONVENTION_ARM_64: > - return qcom_scm_call_smccc(dev, desc, true); > - case SMC_CONVENTION_LEGACY: > - return qcom_scm_call_atomic_legacy(dev, desc); > - default: > - pr_err("Unknown current SCM calling convention.\n"); > - return -EINVAL; > - } > -} > - > -#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 > -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 > - > -/** > - * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus > - * @entry: Entry point function for the cpus > - * @cpus: The cpumask of cpus that will use the entry point > - * > - * Set the cold boot address of the cpus. Any cpu outside the supported > - * range would be removed from the cpu present mask. > - */ > -int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, > - const cpumask_t *cpus) > -{ > - int flags = 0; > - int cpu; > - int scm_cb_flags[] = { > - QCOM_SCM_FLAG_COLDBOOT_CPU0, > - QCOM_SCM_FLAG_COLDBOOT_CPU1, > - QCOM_SCM_FLAG_COLDBOOT_CPU2, > - QCOM_SCM_FLAG_COLDBOOT_CPU3, > - }; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_ADDR, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - if (!cpus || (cpus && cpumask_empty(cpus))) > - return -EINVAL; > - > - for_each_cpu(cpu, cpus) { > - if (cpu < ARRAY_SIZE(scm_cb_flags)) > - flags |= scm_cb_flags[cpu]; > - else > - set_cpu_present(cpu, false); > - } > - > - desc.args[0] = flags; > - desc.args[1] = virt_to_phys(entry); > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 > -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 > - > -struct qcom_scm_entry { > - int flag; > - void *entry; > -}; > - > -static struct qcom_scm_entry qcom_scm_wb[] = { > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, > - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, > -}; > - > -/** > - * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus > - * @dev: Device pointer > - * @entry: Entry point function for the cpus > - * @cpus: The cpumask of cpus that will use the entry point > - * > - * Set the Linux entry point for the SCM to transfer control to when coming > - * out of a power down. CPU power down may be executed on cpuidle or hotplug. > - */ > -int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, > - const cpumask_t *cpus) > -{ > - int ret; > - int flags = 0; > - int cpu; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_ADDR, > - }; > - > - /* > - * Reassign only if we are switching from hotplug entry point > - * to cpuidle entry point or vice versa. > - */ > - for_each_cpu(cpu, cpus) { > - if (entry == qcom_scm_wb[cpu].entry) > - continue; > - flags |= qcom_scm_wb[cpu].flag; > - } > - > - /* No change in entry function */ > - if (!flags) > - return 0; > - > - desc.args[0] = flags; > - desc.args[1] = virt_to_phys(entry); > - ret = qcom_scm_call(dev, &desc); > - if (!ret) { > - for_each_cpu(cpu, cpus) > - qcom_scm_wb[cpu].entry = entry; > - } > - > - return ret; > -} > - > -/** > - * qcom_scm_cpu_power_down() - Power down the cpu > - * @flags - Flags to flush cache > - * > - * This is an end point to power down cpu. If there was a pending interrupt, > - * the control would return from this function, otherwise, the cpu jumps to the > - * warm boot entry point set for this cpu upon reset. > - */ > -void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_TERMINATE_PC, > - .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, > - .arginfo = QCOM_SCM_ARGS(1), > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - qcom_scm_call_atomic(dev, &desc); > -} > - > -int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = state; > - desc.args[1] = id; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_set_dload_mode(struct device *dev, bool enable) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_BOOT, > - .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; > - desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? false : !!desc.res[0]; > -} > - > -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, > - dma_addr_t metadata_phys) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.args[1] = metadata_phys; > - desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, > - phys_addr_t addr, phys_addr_t size) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.args[1] = addr; > - desc.args[2] = size; > - desc.arginfo = QCOM_SCM_ARGS(3); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = peripheral; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_PIL, > - .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = reset; > - desc.args[1] = 0; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, > - unsigned int *val) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_IO, > - .cmd = QCOM_SCM_IO_READ, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = addr; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call_atomic(dev, &desc); > - if (ret >= 0) > - *val = desc.res[0]; > - > - return ret < 0 ? ret : 0; > -} > - > -int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_IO, > - .cmd = QCOM_SCM_IO_WRITE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = addr; > - desc.args[1] = val; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -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, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.arginfo = QCOM_SCM_ARGS(1); > - switch (qcom_smc_convention) { > - case SMC_CONVENTION_ARM_32: > - case SMC_CONVENTION_ARM_64: > - desc.args[0] = SMCCC_FUNCNUM(svc_id, cmd_id) | > - (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); > - break; > - case SMC_CONVENTION_LEGACY: > - desc.args[0] = LEGACY_FUNCNUM(svc_id, cmd_id); > - break; > - default: > - pr_err("Unknown SMC convention being used\n"); > - return -EINVAL; > - } > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_MP, > - .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = device_id; > - desc.args[1] = spare; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, > - size_t *size) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_MP, > - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = spare; > - desc.arginfo = QCOM_SCM_ARGS(1); > - > - ret = qcom_scm_call(dev, &desc); > - > - if (size) > - *size = desc.res[0]; > - > - return ret ? : desc.res[1]; > -} > - > -int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, > - u32 spare) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_MP, > - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - int ret; > - > - desc.args[0] = addr; > - desc.args[1] = size; > - desc.args[2] = spare; > - desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, > - QCOM_SCM_VAL); > - > - ret = qcom_scm_call(dev, &desc); > - > - /* the pg table has been initialized already, ignore the error */ > - if (ret == -EPERM) > - ret = 0; > - > - return ret; > -} > - > -int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, > - size_t mem_sz, phys_addr_t src, size_t src_sz, > - phys_addr_t dest, size_t dest_sz) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_MP, > - .cmd = QCOM_SCM_MP_ASSIGN, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = mem_region; > - desc.args[1] = mem_sz; > - desc.args[2] = src; > - desc.args[3] = src_sz; > - desc.args[4] = dest; > - desc.args[5] = dest_sz; > - desc.args[6] = 0; > - > - desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, > - QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, > - QCOM_SCM_VAL, QCOM_SCM_VAL); > - > - ret = qcom_scm_call(dev, &desc); > - > - return ret ? : desc.res[0]; > -} > - > -int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, > - u32 req_cnt, u32 *resp) > -{ > - int ret; > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_HDCP, > - .cmd = QCOM_SCM_HDCP_INVOKE, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) > - return -ERANGE; > - > - desc.args[0] = req[0].addr; > - desc.args[1] = req[0].val; > - desc.args[2] = req[1].addr; > - desc.args[3] = req[1].val; > - desc.args[4] = req[2].addr; > - desc.args[5] = req[2].val; > - desc.args[6] = req[3].addr; > - desc.args[7] = req[3].val; > - desc.args[8] = req[4].addr; > - desc.args[9] = req[4].val; > - desc.arginfo = QCOM_SCM_ARGS(10); > - > - ret = qcom_scm_call(dev, &desc); > - *resp = desc.res[0]; > - > - return ret; > -} > - > -int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) > -{ > - struct qcom_scm_desc desc = { > - .svc = QCOM_SCM_SVC_SMMU_PROGRAM, > - .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, > - .owner = ARM_SMCCC_OWNER_SIP, > - }; > - > - desc.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL; > - desc.args[1] = en; > - desc.arginfo = QCOM_SCM_ARGS(2); > - > - return qcom_scm_call_atomic(dev, &desc); > -} > - > -void __qcom_scm_init(void) > -{ > - qcom_smc_convention = SMC_CONVENTION_LEGACY; > - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > - goto out; > - > - qcom_smc_convention = SMC_CONVENTION_ARM_64; > - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > - goto out; > - > - qcom_smc_convention = SMC_CONVENTION_ARM_32; > - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > - goto out; > - > - qcom_smc_convention = SMC_CONVENTION_UNKNOWN; > -out: > - pr_debug("QCOM SCM SMC Convention: %d\n", qcom_smc_convention); > -} > diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c > new file mode 100644 > index 0000000..355d9d7 > --- /dev/null > +++ b/drivers/firmware/qcom_scm-smc.c > @@ -0,0 +1,949 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. > + */ > + > +#include <linux/io.h> > +#include <linux/errno.h> > +#include <linux/delay.h> > +#include <linux/mutex.h> > +#include <linux/slab.h> > +#include <linux/types.h> > +#include <linux/qcom_scm.h> > +#include <linux/arm-smccc.h> > +#include <linux/dma-mapping.h> > + > +#include "qcom_scm.h" > + > +#define MAX_QCOM_SCM_ARGS 10 > +#define MAX_QCOM_SCM_RETS 3 > + > +enum qcom_scm_arg_types { > + QCOM_SCM_VAL, > + QCOM_SCM_RO, > + QCOM_SCM_RW, > + QCOM_SCM_BUFVAL, > +}; > + > +#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ > + (((a) & 0x3) << 4) | \ > + (((b) & 0x3) << 6) | \ > + (((c) & 0x3) << 8) | \ > + (((d) & 0x3) << 10) | \ > + (((e) & 0x3) << 12) | \ > + (((f) & 0x3) << 14) | \ > + (((g) & 0x3) << 16) | \ > + (((h) & 0x3) << 18) | \ > + (((i) & 0x3) << 20) | \ > + (((j) & 0x3) << 22) | \ > + ((num) & 0xf)) > + > +#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) > + > +/** > + * struct qcom_scm_desc > + * @arginfo: Metadata describing the arguments in args[] > + * @args: The array of arguments for the secure syscall > + * @res: The values returned by the secure syscall > + */ > +struct qcom_scm_desc { > + u32 svc; > + u32 cmd; > + u32 arginfo; > + u64 args[MAX_QCOM_SCM_ARGS]; > + u64 res[MAX_QCOM_SCM_RETS]; > + u32 owner; > +}; > + > +struct arm_smccc_args { > + unsigned long a[8]; > +}; > + > +enum qcom_smc_convention { > + SMC_CONVENTION_UNKNOWN, > + SMC_CONVENTION_LEGACY, > + SMC_CONVENTION_ARM_32, > + SMC_CONVENTION_ARM_64, > +}; > + > +static enum qcom_smc_convention qcom_smc_convention = SMC_CONVENTION_UNKNOWN; > +static DEFINE_MUTEX(qcom_scm_lock); > + > +#define QCOM_SCM_EBUSY_WAIT_MS 30 > +#define QCOM_SCM_EBUSY_MAX_RETRY 20 > + > +#define SMCCC_FUNCNUM(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) > +#define SMCCC_N_REG_ARGS 4 > +#define SMCCC_FIRST_REG_IDX 2 > +#define SMCCC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SMCCC_N_REG_ARGS + 1) > +#define SMCCC_LAST_REG_IDX (SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1) > + > +static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc, > + struct arm_smccc_res *res) > +{ > + unsigned long a0 = smc->a[0]; > + struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; > + > + quirk.state.a6 = 0; > + > + do { > + arm_smccc_smc_quirk(a0, smc->a[1], smc->a[2], smc->a[3], > + smc->a[4], smc->a[5], quirk.state.a6, > + smc->a[7], res, &quirk); > + > + if (res->a0 == QCOM_SCM_INTERRUPTED) > + a0 = res->a0; > + > + } while (res->a0 == QCOM_SCM_INTERRUPTED); > +} > + > +static int qcom_scm_call_smccc(struct device *dev, > + struct qcom_scm_desc *desc, bool atomic) > +{ > + int arglen = desc->arginfo & 0xf; > + int i; > + dma_addr_t args_phys = 0; > + void *args_virt = NULL; > + size_t alloc_len; > + gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; > + struct arm_smccc_res res; > + struct arm_smccc_args smc = {0}; > + > + smc.a[0] = ARM_SMCCC_CALL_VAL( > + atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL, > + (qcom_smc_convention == SMC_CONVENTION_ARM_64) ? > + ARM_SMCCC_SMC_64 : > + ARM_SMCCC_SMC_32, > + desc->owner, > + SMCCC_FUNCNUM(desc->svc, desc->cmd)); > + smc.a[1] = desc->arginfo; > + for (i = 0; i < SMCCC_N_REG_ARGS; i++) > + smc.a[i + SMCCC_FIRST_REG_IDX] = desc->args[i]; > + > + if (unlikely(arglen > SMCCC_N_REG_ARGS)) { > + alloc_len = SMCCC_N_EXT_ARGS * sizeof(u64); > + args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); > + > + if (!args_virt) > + return -ENOMEM; > + > + if (qcom_smc_convention == SMC_CONVENTION_ARM_32) { > + __le32 *args = args_virt; > + > + for (i = 0; i < SMCCC_N_EXT_ARGS; i++) > + args[i] = cpu_to_le32(desc->args[i + > + SMCCC_N_REG_ARGS - 1]); > + } else { > + __le64 *args = args_virt; > + > + for (i = 0; i < SMCCC_N_EXT_ARGS; i++) > + args[i] = cpu_to_le64(desc->args[i + > + SMCCC_N_REG_ARGS - 1]); > + } > + > + args_phys = dma_map_single(dev, args_virt, alloc_len, > + DMA_TO_DEVICE); > + > + if (dma_mapping_error(dev, args_phys)) { > + kfree(args_virt); > + return -ENOMEM; > + } > + > + smc.a[SMCCC_LAST_REG_IDX] = args_phys; > + } > + > + if (atomic) { > + __qcom_scm_call_do_quirk(&smc, &res); > + } else { > + int retry_count = 0; > + > + do { > + mutex_lock(&qcom_scm_lock); > + > + __qcom_scm_call_do_quirk(&smc, &res); > + > + mutex_unlock(&qcom_scm_lock); > + > + if (res.a0 == QCOM_SCM_V2_EBUSY) { > + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) > + break; > + msleep(QCOM_SCM_EBUSY_WAIT_MS); > + } > + } while (res.a0 == QCOM_SCM_V2_EBUSY); > + } > + > + if (args_virt) { > + dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); > + kfree(args_virt); > + } > + > + desc->res[0] = res.a1; > + desc->res[1] = res.a2; > + desc->res[2] = res.a3; > + > + if (res.a0 < 0) > + return qcom_scm_remap_error(res.a0); > + > + return 0; > +} > + > +#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) > + > +/** > + * struct 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 legacy_get_command_buffer() > + * > + * An SCM command is laid out in memory as follows: > + * > + * ------------------- <--- struct legacy_command > + * | command header | > + * ------------------- <--- legacy_get_command_buffer() > + * | command buffer | > + * ------------------- <--- struct legacy_response and > + * | response header | legacy_command_to_response() > + * ------------------- <--- legacy_get_response_buffer() > + * | response buffer | > + * ------------------- > + * > + * There can be arbitrary padding between the headers and buffers so > + * you should always use the appropriate qcom_scm_get_*_buffer() routines > + * to access the buffers in a safe manner. > + */ > +struct legacy_command { > + __le32 len; > + __le32 buf_offset; > + __le32 resp_hdr_offset; > + __le32 id; > + __le32 buf[0]; > +}; > + > +/** > + * struct legacy_response - one SCM response buffer > + * @len: total available memory for response > + * @buf_offset: start of response data relative to start of legacy_response > + * @is_complete: indicates if the command has finished processing > + */ > +struct legacy_response { > + __le32 len; > + __le32 buf_offset; > + __le32 is_complete; > +}; > + > +/** > + * legacy_command_to_response() - Get a pointer to a legacy_response > + * @cmd: command > + * > + * Returns a pointer to a response for a command. > + */ > +static inline struct legacy_response *legacy_command_to_response( > + const struct legacy_command *cmd) > +{ > + return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); > +} > + > +/** > + * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) > +{ > + return (void *)cmd->buf; > +} > + > +/** > + * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) > +{ > + return (void *)rsp + le32_to_cpu(rsp->buf_offset); > +} > + > +static void __qcom_scm_call_do(const struct arm_smccc_args *smc, > + struct arm_smccc_res *res) > +{ > + do { > + arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], > + smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); > + } while (res->a0 == QCOM_SCM_INTERRUPTED); > +} > + > +/** > + * qcom_scm_call_legacy() - Send an SCM command > + * @dev: struct device > + * @svc_id: service identifier > + * @cmd_id: command identifier > + * @cmd_buf: command buffer > + * @cmd_len: length of the command buffer > + * @resp_buf: response buffer > + * @resp_len: length of the response buffer > + * > + * 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. > + */ > +static int qcom_scm_call_legacy(struct device *dev, struct qcom_scm_desc *desc) > +{ > + int arglen = desc->arginfo & 0xf; > + int ret = 0, context_id; > + size_t i; > + struct legacy_command *cmd; > + struct legacy_response *rsp; > + struct arm_smccc_args smc = {0}; > + struct arm_smccc_res 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; > + __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); > + > + arg_buf = legacy_get_command_buffer(cmd); > + for (i = 0; i < arglen; i++) > + arg_buf[i] = cpu_to_le32(desc->args[i]); > + > + rsp = 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.a[0] = 1; > + smc.a[1] = (unsigned long)&context_id; > + smc.a[2] = cmd_phys; > + > + mutex_lock(&qcom_scm_lock); > + __qcom_scm_call_do(&smc, &res); > + if (res.a0 < 0) > + ret = qcom_scm_remap_error(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); > + > + res_buf = legacy_get_response_buffer(rsp); > + for (i = 0; i < MAX_QCOM_SCM_RETS; i++) > + desc->res[i] = le32_to_cpu(res_buf[i]); > +out: > + dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); > + kfree(cmd); > + return ret; > +} > + > +#define LEGACY_ATOMIC_N_REG_ARGS 5 > +#define LEGACY_ATOMIC_FIRST_REG_IDX 2 > +#define LEGACY_CLASS_REGISTER (0x2 << 8) > +#define LEGACY_MASK_IRQS BIT(5) > +#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ > + LEGACY_CLASS_REGISTER | \ > + LEGACY_MASK_IRQS | \ > + (n & 0xf)) > + > +/** > + * qcom_scm_call_atomic_legacy() - Send an atomic SCM command with up to > + * 5 arguments and 3 return values > + * > + * This shall only be used with commands that are guaranteed to be > + * uninterruptable, atomic and SMP safe. > + */ > +static int qcom_scm_call_atomic_legacy(struct device *dev, > + struct qcom_scm_desc *desc) > +{ > + int context_id; > + struct arm_smccc_args smc = {0}; > + struct arm_smccc_res res; > + size_t i, arglen = desc->arginfo & 0xf; > + > + BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); > + > + smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); > + smc.a[1] = (unsigned long)&context_id; > + > + for (i = 0; i < arglen; i++) > + smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; > + > + arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], > + smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); > + > + desc->res[0] = res.a1; > + desc->res[1] = res.a2; > + desc->res[2] = res.a3; > + > + return res.a0; > +} > + > +/** > + * qcom_scm_call() - Invoke a syscall in the secure world > + * @dev: device > + * @svc_id: service identifier > + * @cmd_id: command identifier > + * @desc: Descriptor structure containing arguments and return values > + * > + * Sends a command to the SCM and waits for the command to finish processing. > + * This should *only* be called in pre-emptible context. > + */ > +static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) > +{ > + might_sleep(); > + switch (qcom_smc_convention) { > + case SMC_CONVENTION_ARM_32: > + case SMC_CONVENTION_ARM_64: > + return qcom_scm_call_smccc(dev, desc, false); > + case SMC_CONVENTION_LEGACY: > + return qcom_scm_call_legacy(dev, desc); > + default: > + pr_err("Unknown current SCM calling convention.\n"); > + return -EINVAL; > + } > +} > + > +/** > + * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() > + * @dev: device > + * @svc_id: service identifier > + * @cmd_id: command identifier > + * @desc: Descriptor structure containing arguments and return values > + * @res: Structure containing results from SMC/HVC call > + * > + * Sends a command to the SCM and waits for the command to finish processing. > + * This can be called in atomic context. > + */ > +static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) > +{ > + switch (qcom_smc_convention) { > + case SMC_CONVENTION_ARM_32: > + case SMC_CONVENTION_ARM_64: > + return qcom_scm_call_smccc(dev, desc, true); > + case SMC_CONVENTION_LEGACY: > + return qcom_scm_call_atomic_legacy(dev, desc); > + default: > + pr_err("Unknown current SCM calling convention.\n"); > + return -EINVAL; > + } > +} > + > +#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 > +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 > +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 > +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 > + > +/** > + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus > + * @entry: Entry point function for the cpus > + * @cpus: The cpumask of cpus that will use the entry point > + * > + * Set the cold boot address of the cpus. Any cpu outside the supported > + * range would be removed from the cpu present mask. > + */ > +int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, > + const cpumask_t *cpus) > +{ > + int flags = 0; > + int cpu; > + int scm_cb_flags[] = { > + QCOM_SCM_FLAG_COLDBOOT_CPU0, > + QCOM_SCM_FLAG_COLDBOOT_CPU1, > + QCOM_SCM_FLAG_COLDBOOT_CPU2, > + QCOM_SCM_FLAG_COLDBOOT_CPU3, > + }; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_BOOT, > + .cmd = QCOM_SCM_BOOT_SET_ADDR, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + if (!cpus || (cpus && cpumask_empty(cpus))) > + return -EINVAL; > + > + for_each_cpu(cpu, cpus) { > + if (cpu < ARRAY_SIZE(scm_cb_flags)) > + flags |= scm_cb_flags[cpu]; > + else > + set_cpu_present(cpu, false); > + } > + > + desc.args[0] = flags; > + desc.args[1] = virt_to_phys(entry); > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + return qcom_scm_call_atomic(dev, &desc); > +} > + > +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 > +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 > +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 > +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 > + > +struct qcom_scm_entry { > + int flag; > + void *entry; > +}; > + > +static struct qcom_scm_entry qcom_scm_wb[] = { > + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, > + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, > + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, > + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, > +}; > + > +/** > + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus > + * @dev: Device pointer > + * @entry: Entry point function for the cpus > + * @cpus: The cpumask of cpus that will use the entry point > + * > + * Set the Linux entry point for the SCM to transfer control to when coming > + * out of a power down. CPU power down may be executed on cpuidle or hotplug. > + */ > +int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, > + const cpumask_t *cpus) > +{ > + int ret; > + int flags = 0; > + int cpu; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_BOOT, > + .cmd = QCOM_SCM_BOOT_SET_ADDR, > + }; > + > + /* > + * Reassign only if we are switching from hotplug entry point > + * to cpuidle entry point or vice versa. > + */ > + for_each_cpu(cpu, cpus) { > + if (entry == qcom_scm_wb[cpu].entry) > + continue; > + flags |= qcom_scm_wb[cpu].flag; > + } > + > + /* No change in entry function */ > + if (!flags) > + return 0; > + > + desc.args[0] = flags; > + desc.args[1] = virt_to_phys(entry); > + ret = qcom_scm_call(dev, &desc); > + if (!ret) { > + for_each_cpu(cpu, cpus) > + qcom_scm_wb[cpu].entry = entry; > + } > + > + return ret; > +} > + > +/** > + * qcom_scm_cpu_power_down() - Power down the cpu > + * @flags - Flags to flush cache > + * > + * This is an end point to power down cpu. If there was a pending interrupt, > + * the control would return from this function, otherwise, the cpu jumps to the > + * warm boot entry point set for this cpu upon reset. > + */ > +void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_BOOT, > + .cmd = QCOM_SCM_BOOT_TERMINATE_PC, > + .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, > + .arginfo = QCOM_SCM_ARGS(1), > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + qcom_scm_call_atomic(dev, &desc); > +} > + > +int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_BOOT, > + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = state; > + desc.args[1] = id; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_set_dload_mode(struct device *dev, bool enable) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_BOOT, > + .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; > + desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + return qcom_scm_call_atomic(dev, &desc); > +} > + > +bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = peripheral; > + desc.arginfo = QCOM_SCM_ARGS(1); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? false : !!desc.res[0]; > +} > + > +int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, > + dma_addr_t metadata_phys) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = peripheral; > + desc.args[1] = metadata_phys; > + desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, > + phys_addr_t addr, phys_addr_t size) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = peripheral; > + desc.args[1] = addr; > + desc.args[2] = size; > + desc.arginfo = QCOM_SCM_ARGS(3); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = peripheral; > + desc.arginfo = QCOM_SCM_ARGS(1); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = peripheral; > + desc.arginfo = QCOM_SCM_ARGS(1); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_PIL, > + .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = reset; > + desc.args[1] = 0; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, > + unsigned int *val) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_IO, > + .cmd = QCOM_SCM_IO_READ, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = addr; > + desc.arginfo = QCOM_SCM_ARGS(1); > + > + ret = qcom_scm_call_atomic(dev, &desc); > + if (ret >= 0) > + *val = desc.res[0]; > + > + return ret < 0 ? ret : 0; > +} > + > +int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_IO, > + .cmd = QCOM_SCM_IO_WRITE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = addr; > + desc.args[1] = val; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + return qcom_scm_call_atomic(dev, &desc); > +} > + > +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, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.arginfo = QCOM_SCM_ARGS(1); > + switch (qcom_smc_convention) { > + case SMC_CONVENTION_ARM_32: > + case SMC_CONVENTION_ARM_64: > + desc.args[0] = SMCCC_FUNCNUM(svc_id, cmd_id) | > + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); > + break; > + case SMC_CONVENTION_LEGACY: > + desc.args[0] = LEGACY_FUNCNUM(svc_id, cmd_id); > + break; > + default: > + pr_err("Unknown SMC convention being used\n"); > + return -EINVAL; > + } > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_MP, > + .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = device_id; > + desc.args[1] = spare; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, > + size_t *size) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_MP, > + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = spare; > + desc.arginfo = QCOM_SCM_ARGS(1); > + > + ret = qcom_scm_call(dev, &desc); > + > + if (size) > + *size = desc.res[0]; > + > + return ret ? : desc.res[1]; > +} > + > +int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, > + u32 spare) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_MP, > + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + int ret; > + > + desc.args[0] = addr; > + desc.args[1] = size; > + desc.args[2] = spare; > + desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, > + QCOM_SCM_VAL); > + > + ret = qcom_scm_call(dev, &desc); > + > + /* the pg table has been initialized already, ignore the error */ > + if (ret == -EPERM) > + ret = 0; > + > + return ret; > +} > + > +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, > + size_t mem_sz, phys_addr_t src, size_t src_sz, > + phys_addr_t dest, size_t dest_sz) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_MP, > + .cmd = QCOM_SCM_MP_ASSIGN, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = mem_region; > + desc.args[1] = mem_sz; > + desc.args[2] = src; > + desc.args[3] = src_sz; > + desc.args[4] = dest; > + desc.args[5] = dest_sz; > + desc.args[6] = 0; > + > + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, > + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, > + QCOM_SCM_VAL, QCOM_SCM_VAL); > + > + ret = qcom_scm_call(dev, &desc); > + > + return ret ? : desc.res[0]; > +} > + > +int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, > + u32 req_cnt, u32 *resp) > +{ > + int ret; > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_HDCP, > + .cmd = QCOM_SCM_HDCP_INVOKE, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) > + return -ERANGE; > + > + desc.args[0] = req[0].addr; > + desc.args[1] = req[0].val; > + desc.args[2] = req[1].addr; > + desc.args[3] = req[1].val; > + desc.args[4] = req[2].addr; > + desc.args[5] = req[2].val; > + desc.args[6] = req[3].addr; > + desc.args[7] = req[3].val; > + desc.args[8] = req[4].addr; > + desc.args[9] = req[4].val; > + desc.arginfo = QCOM_SCM_ARGS(10); > + > + ret = qcom_scm_call(dev, &desc); > + *resp = desc.res[0]; > + > + return ret; > +} > + > +int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) > +{ > + struct qcom_scm_desc desc = { > + .svc = QCOM_SCM_SVC_SMMU_PROGRAM, > + .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, > + .owner = ARM_SMCCC_OWNER_SIP, > + }; > + > + desc.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL; > + desc.args[1] = en; > + desc.arginfo = QCOM_SCM_ARGS(2); > + > + return qcom_scm_call_atomic(dev, &desc); > +} > + > +void __qcom_scm_init(void) > +{ > + qcom_smc_convention = SMC_CONVENTION_LEGACY; > + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > + goto out; > + > + qcom_smc_convention = SMC_CONVENTION_ARM_64; > + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > + goto out; > + > + qcom_smc_convention = SMC_CONVENTION_ARM_32; > + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, > + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) > + goto out; > + > + qcom_smc_convention = SMC_CONVENTION_UNKNOWN; > +out: > + pr_debug("QCOM SCM SMC Convention: %d\n", qcom_smc_convention); > +} > -- > The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, > a Linux Foundation Collaborative Project >
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index e40a77b..ad477ca 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -236,16 +236,20 @@ config INTEL_STRATIX10_RSU config QCOM_SCM bool - depends on ARM || ARM64 + depends on QCOM_SCM_TRANSPORT_SMC select RESET_CONTROLLER + help + This option enables support for communicating with + Qualcomm secure worlds. -config QCOM_SCM_32 - def_bool y - depends on QCOM_SCM && ARM - -config QCOM_SCM_64 +config QCOM_SCM_TRANSPORT_SMC def_bool y - depends on QCOM_SCM && ARM64 + depends on ARM || ARM64 + help + This option enables support for communicating with + Qualcomm secure worlds via the SMC instruction, using + either ARM SMCCC or legacy calling convention by querying + the Qualcomm secure world. config QCOM_SCM_DOWNLOAD_MODE_DEFAULT bool "Qualcomm download mode enabled by default" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3fcb919..fc250f0 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -18,9 +18,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_QCOM_SCM) += qcom_scm.o -obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o -obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o -CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a +obj-$(CONFIG_QCOM_SCM_TRANSPORT_SMC) += qcom_scm-smc.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c deleted file mode 100644 index 3325c21..0000000 --- a/drivers/firmware/qcom_scm-32.c +++ /dev/null @@ -1,660 +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 <linux/slab.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/qcom_scm.h> -#include <linux/arm-smccc.h> -#include <linux/dma-mapping.h> - -#include "qcom_scm.h" - -#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 - -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 - -struct qcom_scm_entry { - int flag; - void *entry; -}; - -static struct qcom_scm_entry qcom_scm_wb[] = { - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, -}; - -static DEFINE_MUTEX(qcom_scm_lock); - -#define MAX_QCOM_SCM_ARGS 10 -#define MAX_QCOM_SCM_RETS 3 - -enum qcom_scm_arg_types { - QCOM_SCM_VAL, - QCOM_SCM_RO, - QCOM_SCM_RW, - QCOM_SCM_BUFVAL, -}; - -#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ - (((a) & 0x3) << 4) | \ - (((b) & 0x3) << 6) | \ - (((c) & 0x3) << 8) | \ - (((d) & 0x3) << 10) | \ - (((e) & 0x3) << 12) | \ - (((f) & 0x3) << 14) | \ - (((g) & 0x3) << 16) | \ - (((h) & 0x3) << 18) | \ - (((i) & 0x3) << 20) | \ - (((j) & 0x3) << 22) | \ - ((num) & 0xf)) - -#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -/** - * struct qcom_scm_desc - * @arginfo: Metadata describing the arguments in args[] - * @args: The array of arguments for the secure syscall - * @res: The values returned by the secure syscall - */ -struct qcom_scm_desc { - u32 svc; - u32 cmd; - u32 arginfo; - u64 args[MAX_QCOM_SCM_ARGS]; - u64 res[MAX_QCOM_SCM_RETS]; - u32 owner; -}; -struct arm_smccc_args { - unsigned long a[8]; -}; - -#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) - -/** - * struct 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 legacy_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct legacy_command - * | command header | - * ------------------- <--- legacy_get_command_buffer() - * | command buffer | - * ------------------- <--- struct legacy_response and - * | response header | legacy_command_to_response() - * ------------------- <--- legacy_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate qcom_scm_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct legacy_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[0]; -}; - -/** - * struct legacy_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of legacy_response - * @is_complete: indicates if the command has finished processing - */ -struct legacy_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * legacy_command_to_response() - Get a pointer to a legacy_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct legacy_response *legacy_command_to_response( - const struct legacy_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static void __qcom_scm_call_do(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - do { - arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], - smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -/** - * qcom_scm_call() - Send an SCM command - * @dev: struct device - * @svc_id: service identifier - * @cmd_id: command identifier - * @cmd_buf: command buffer - * @cmd_len: length of the command buffer - * @resp_buf: response buffer - * @resp_len: length of the response buffer - * - * 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. - */ -static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) -{ - int arglen = desc->arginfo & 0xf; - int ret = 0, context_id; - size_t i; - struct legacy_command *cmd; - struct legacy_response *rsp; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res 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; - __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); - - arg_buf = legacy_get_command_buffer(cmd); - for (i = 0; i < arglen; i++) - arg_buf[i] = cpu_to_le32(desc->args[i]); - - rsp = 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.a[0] = 1; - smc.a[1] = (unsigned long)&context_id; - smc.a[2] = cmd_phys; - - mutex_lock(&qcom_scm_lock); - __qcom_scm_call_do(&smc, &res); - if (res.a0 < 0) - ret = qcom_scm_remap_error(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); - - res_buf = legacy_get_response_buffer(rsp); - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) - desc->res[i] = le32_to_cpu(res_buf[i]); -out: - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); - kfree(cmd); - return ret; -} - -#define LEGACY_ATOMIC_N_REG_ARGS 5 -#define LEGACY_ATOMIC_FIRST_REG_IDX 2 -#define LEGACY_CLASS_REGISTER (0x2 << 8) -#define LEGACY_MASK_IRQS BIT(5) -#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ - LEGACY_CLASS_REGISTER | \ - LEGACY_MASK_IRQS | \ - (n & 0xf)) - -/** - * qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments - * and 3 return values - * - * This shall only be used with commands that are guaranteed to be - * uninterruptable, atomic and SMP safe. - */ -static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) -{ - int context_id; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res res; - size_t i, arglen = desc->arginfo & 0xf; - - BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); - - smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); - smc.a[1] = (unsigned long)&context_id; - - for (i = 0; i < arglen; i++) - smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; - - arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], - smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); - - desc->res[0] = res.a1; - desc->res[1] = res.a2; - desc->res[2] = res.a3; - - return res.a0; -} - -/** - * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus - * @entry: Entry point function for the cpus - * @cpus: The cpumask of cpus that will use the entry point - * - * Set the cold boot address of the cpus. Any cpu outside the supported - * range would be removed from the cpu present mask. - */ -int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, - const cpumask_t *cpus) -{ - int flags = 0; - int cpu; - int scm_cb_flags[] = { - QCOM_SCM_FLAG_COLDBOOT_CPU0, - QCOM_SCM_FLAG_COLDBOOT_CPU1, - QCOM_SCM_FLAG_COLDBOOT_CPU2, - QCOM_SCM_FLAG_COLDBOOT_CPU3, - }; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - if (!cpus || (cpus && cpumask_empty(cpus))) - return -EINVAL; - - for_each_cpu(cpu, cpus) { - if (cpu < ARRAY_SIZE(scm_cb_flags)) - flags |= scm_cb_flags[cpu]; - else - set_cpu_present(cpu, false); - } - - desc.args[0] = flags; - desc.args[1] = virt_to_phys(entry); - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -/** - * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus - * @entry: Entry point function for the cpus - * @cpus: The cpumask of cpus that will use the entry point - * - * Set the Linux entry point for the SCM to transfer control to when coming - * out of a power down. CPU power down may be executed on cpuidle or hotplug. - */ -int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, - const cpumask_t *cpus) -{ - int ret; - int flags = 0; - int cpu; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR, - .arginfo = QCOM_SCM_ARGS(2), - }; - - /* - * Reassign only if we are switching from hotplug entry point - * to cpuidle entry point or vice versa. - */ - for_each_cpu(cpu, cpus) { - if (entry == qcom_scm_wb[cpu].entry) - continue; - flags |= qcom_scm_wb[cpu].flag; - } - - /* No change in entry function */ - if (!flags) - return 0; - - desc.args[0] = flags; - desc.args[1] = virt_to_phys(entry); - ret = qcom_scm_call(dev, &desc); - if (!ret) { - for_each_cpu(cpu, cpus) - qcom_scm_wb[cpu].entry = entry; - } - - return ret; -} - -/** - * qcom_scm_cpu_power_down() - Power down the cpu - * @flags - Flags to flush cache - * - * This is an end point to power down cpu. If there was a pending interrupt, - * the control would return from this function, otherwise, the cpu jumps to the - * warm boot entry point set for this cpu upon reset. - */ -void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_TERMINATE_PC, - .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, - .arginfo = QCOM_SCM_ARGS(1), - .owner = ARM_SMCCC_OWNER_SIP, - }; - - qcom_scm_call_atomic(dev, &desc); -} - -int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = state; - desc.args[1] = id; - desc.arginfo = QCOM_SCM_ARGS(2); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_set_dload_mode(struct device *dev, bool enable) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; - desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - return ret ? false : !!desc.res[0]; -} - -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, - dma_addr_t metadata_phys) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.args[1] = metadata_phys; - desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, - phys_addr_t addr, phys_addr_t size) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.args[1] = addr; - desc.args[2] = size; - desc.arginfo = QCOM_SCM_ARGS(3); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = peripheral; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = reset; - desc.args[1] = 0; - desc.arginfo = QCOM_SCM_ARGS(2); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, - unsigned int *val) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_READ, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = addr; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call_atomic(dev, &desc); - if (ret >= 0) - *val = desc.res[0]; - - return ret < 0 ? ret : 0; -} - -int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_WRITE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = addr; - desc.args[1] = val; - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -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] = (svc_id << 10) | cmd_id, - .arginfo = QCOM_SCM_ARGS(1), - }; - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, - u32 spare) -{ - return -ENODEV; -} - -int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, - size_t *size) -{ - return -ENODEV; -} - -int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, - u32 spare) -{ - return -ENODEV; -} - -int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, - size_t mem_sz, phys_addr_t src, size_t src_sz, - phys_addr_t dest, size_t dest_sz) -{ - return -ENODEV; -} - -int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, - u32 req_cnt, u32 *resp) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_HDCP, - .cmd = QCOM_SCM_HDCP_INVOKE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) - return -ERANGE; - - desc.args[0] = req[0].addr; - desc.args[1] = req[0].val; - desc.args[2] = req[1].addr; - desc.args[3] = req[1].val; - desc.args[4] = req[2].addr; - desc.args[5] = req[2].val; - desc.args[6] = req[3].addr; - desc.args[7] = req[3].val; - desc.args[8] = req[4].addr; - desc.args[9] = req[4].val; - desc.arginfo = QCOM_SCM_ARGS(req_cnt * 2); - - ret = qcom_scm_call(dev, &desc); - *resp = desc.res[0]; - - return ret; -} - -int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool enable) -{ - return -ENODEV; -} - -void __qcom_scm_init(void) -{ -} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c deleted file mode 100644 index 355d9d7..0000000 --- a/drivers/firmware/qcom_scm-64.c +++ /dev/null @@ -1,949 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. - */ - -#include <linux/io.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/qcom_scm.h> -#include <linux/arm-smccc.h> -#include <linux/dma-mapping.h> - -#include "qcom_scm.h" - -#define MAX_QCOM_SCM_ARGS 10 -#define MAX_QCOM_SCM_RETS 3 - -enum qcom_scm_arg_types { - QCOM_SCM_VAL, - QCOM_SCM_RO, - QCOM_SCM_RW, - QCOM_SCM_BUFVAL, -}; - -#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ - (((a) & 0x3) << 4) | \ - (((b) & 0x3) << 6) | \ - (((c) & 0x3) << 8) | \ - (((d) & 0x3) << 10) | \ - (((e) & 0x3) << 12) | \ - (((f) & 0x3) << 14) | \ - (((g) & 0x3) << 16) | \ - (((h) & 0x3) << 18) | \ - (((i) & 0x3) << 20) | \ - (((j) & 0x3) << 22) | \ - ((num) & 0xf)) - -#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -/** - * struct qcom_scm_desc - * @arginfo: Metadata describing the arguments in args[] - * @args: The array of arguments for the secure syscall - * @res: The values returned by the secure syscall - */ -struct qcom_scm_desc { - u32 svc; - u32 cmd; - u32 arginfo; - u64 args[MAX_QCOM_SCM_ARGS]; - u64 res[MAX_QCOM_SCM_RETS]; - u32 owner; -}; - -struct arm_smccc_args { - unsigned long a[8]; -}; - -enum qcom_smc_convention { - SMC_CONVENTION_UNKNOWN, - SMC_CONVENTION_LEGACY, - SMC_CONVENTION_ARM_32, - SMC_CONVENTION_ARM_64, -}; - -static enum qcom_smc_convention qcom_smc_convention = SMC_CONVENTION_UNKNOWN; -static DEFINE_MUTEX(qcom_scm_lock); - -#define QCOM_SCM_EBUSY_WAIT_MS 30 -#define QCOM_SCM_EBUSY_MAX_RETRY 20 - -#define SMCCC_FUNCNUM(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) -#define SMCCC_N_REG_ARGS 4 -#define SMCCC_FIRST_REG_IDX 2 -#define SMCCC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SMCCC_N_REG_ARGS + 1) -#define SMCCC_LAST_REG_IDX (SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1) - -static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - unsigned long a0 = smc->a[0]; - struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; - - quirk.state.a6 = 0; - - do { - arm_smccc_smc_quirk(a0, smc->a[1], smc->a[2], smc->a[3], - smc->a[4], smc->a[5], quirk.state.a6, - smc->a[7], res, &quirk); - - if (res->a0 == QCOM_SCM_INTERRUPTED) - a0 = res->a0; - - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -static int qcom_scm_call_smccc(struct device *dev, - struct qcom_scm_desc *desc, bool atomic) -{ - int arglen = desc->arginfo & 0xf; - int i; - dma_addr_t args_phys = 0; - void *args_virt = NULL; - size_t alloc_len; - gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; - struct arm_smccc_res res; - struct arm_smccc_args smc = {0}; - - smc.a[0] = ARM_SMCCC_CALL_VAL( - atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL, - (qcom_smc_convention == SMC_CONVENTION_ARM_64) ? - ARM_SMCCC_SMC_64 : - ARM_SMCCC_SMC_32, - desc->owner, - SMCCC_FUNCNUM(desc->svc, desc->cmd)); - smc.a[1] = desc->arginfo; - for (i = 0; i < SMCCC_N_REG_ARGS; i++) - smc.a[i + SMCCC_FIRST_REG_IDX] = desc->args[i]; - - if (unlikely(arglen > SMCCC_N_REG_ARGS)) { - alloc_len = SMCCC_N_EXT_ARGS * sizeof(u64); - args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); - - if (!args_virt) - return -ENOMEM; - - if (qcom_smc_convention == SMC_CONVENTION_ARM_32) { - __le32 *args = args_virt; - - for (i = 0; i < SMCCC_N_EXT_ARGS; i++) - args[i] = cpu_to_le32(desc->args[i + - SMCCC_N_REG_ARGS - 1]); - } else { - __le64 *args = args_virt; - - for (i = 0; i < SMCCC_N_EXT_ARGS; i++) - args[i] = cpu_to_le64(desc->args[i + - SMCCC_N_REG_ARGS - 1]); - } - - args_phys = dma_map_single(dev, args_virt, alloc_len, - DMA_TO_DEVICE); - - if (dma_mapping_error(dev, args_phys)) { - kfree(args_virt); - return -ENOMEM; - } - - smc.a[SMCCC_LAST_REG_IDX] = args_phys; - } - - if (atomic) { - __qcom_scm_call_do_quirk(&smc, &res); - } else { - int retry_count = 0; - - do { - mutex_lock(&qcom_scm_lock); - - __qcom_scm_call_do_quirk(&smc, &res); - - mutex_unlock(&qcom_scm_lock); - - if (res.a0 == QCOM_SCM_V2_EBUSY) { - if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) - break; - msleep(QCOM_SCM_EBUSY_WAIT_MS); - } - } while (res.a0 == QCOM_SCM_V2_EBUSY); - } - - if (args_virt) { - dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); - kfree(args_virt); - } - - desc->res[0] = res.a1; - desc->res[1] = res.a2; - desc->res[2] = res.a3; - - if (res.a0 < 0) - return qcom_scm_remap_error(res.a0); - - return 0; -} - -#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) - -/** - * struct 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 legacy_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct legacy_command - * | command header | - * ------------------- <--- legacy_get_command_buffer() - * | command buffer | - * ------------------- <--- struct legacy_response and - * | response header | legacy_command_to_response() - * ------------------- <--- legacy_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate qcom_scm_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct legacy_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[0]; -}; - -/** - * struct legacy_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of legacy_response - * @is_complete: indicates if the command has finished processing - */ -struct legacy_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * legacy_command_to_response() - Get a pointer to a legacy_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct legacy_response *legacy_command_to_response( - const struct legacy_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static void __qcom_scm_call_do(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - do { - arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], - smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -/** - * qcom_scm_call_legacy() - Send an SCM command - * @dev: struct device - * @svc_id: service identifier - * @cmd_id: command identifier - * @cmd_buf: command buffer - * @cmd_len: length of the command buffer - * @resp_buf: response buffer - * @resp_len: length of the response buffer - * - * 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. - */ -static int qcom_scm_call_legacy(struct device *dev, struct qcom_scm_desc *desc) -{ - int arglen = desc->arginfo & 0xf; - int ret = 0, context_id; - size_t i; - struct legacy_command *cmd; - struct legacy_response *rsp; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res 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; - __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); - - arg_buf = legacy_get_command_buffer(cmd); - for (i = 0; i < arglen; i++) - arg_buf[i] = cpu_to_le32(desc->args[i]); - - rsp = 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.a[0] = 1; - smc.a[1] = (unsigned long)&context_id; - smc.a[2] = cmd_phys; - - mutex_lock(&qcom_scm_lock); - __qcom_scm_call_do(&smc, &res); - if (res.a0 < 0) - ret = qcom_scm_remap_error(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); - - res_buf = legacy_get_response_buffer(rsp); - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) - desc->res[i] = le32_to_cpu(res_buf[i]); -out: - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); - kfree(cmd); - return ret; -} - -#define LEGACY_ATOMIC_N_REG_ARGS 5 -#define LEGACY_ATOMIC_FIRST_REG_IDX 2 -#define LEGACY_CLASS_REGISTER (0x2 << 8) -#define LEGACY_MASK_IRQS BIT(5) -#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ - LEGACY_CLASS_REGISTER | \ - LEGACY_MASK_IRQS | \ - (n & 0xf)) - -/** - * qcom_scm_call_atomic_legacy() - Send an atomic SCM command with up to - * 5 arguments and 3 return values - * - * This shall only be used with commands that are guaranteed to be - * uninterruptable, atomic and SMP safe. - */ -static int qcom_scm_call_atomic_legacy(struct device *dev, - struct qcom_scm_desc *desc) -{ - int context_id; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res res; - size_t i, arglen = desc->arginfo & 0xf; - - BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); - - smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); - smc.a[1] = (unsigned long)&context_id; - - for (i = 0; i < arglen; i++) - smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; - - arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], - smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); - - desc->res[0] = res.a1; - desc->res[1] = res.a2; - desc->res[2] = res.a3; - - return res.a0; -} - -/** - * qcom_scm_call() - Invoke a syscall in the secure world - * @dev: device - * @svc_id: service identifier - * @cmd_id: command identifier - * @desc: Descriptor structure containing arguments and return values - * - * Sends a command to the SCM and waits for the command to finish processing. - * This should *only* be called in pre-emptible context. - */ -static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) -{ - might_sleep(); - switch (qcom_smc_convention) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - return qcom_scm_call_smccc(dev, desc, false); - case SMC_CONVENTION_LEGACY: - return qcom_scm_call_legacy(dev, desc); - default: - pr_err("Unknown current SCM calling convention.\n"); - return -EINVAL; - } -} - -/** - * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() - * @dev: device - * @svc_id: service identifier - * @cmd_id: command identifier - * @desc: Descriptor structure containing arguments and return values - * @res: Structure containing results from SMC/HVC call - * - * Sends a command to the SCM and waits for the command to finish processing. - * This can be called in atomic context. - */ -static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) -{ - switch (qcom_smc_convention) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - return qcom_scm_call_smccc(dev, desc, true); - case SMC_CONVENTION_LEGACY: - return qcom_scm_call_atomic_legacy(dev, desc); - default: - pr_err("Unknown current SCM calling convention.\n"); - return -EINVAL; - } -} - -#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 - -/** - * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus - * @entry: Entry point function for the cpus - * @cpus: The cpumask of cpus that will use the entry point - * - * Set the cold boot address of the cpus. Any cpu outside the supported - * range would be removed from the cpu present mask. - */ -int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, - const cpumask_t *cpus) -{ - int flags = 0; - int cpu; - int scm_cb_flags[] = { - QCOM_SCM_FLAG_COLDBOOT_CPU0, - QCOM_SCM_FLAG_COLDBOOT_CPU1, - QCOM_SCM_FLAG_COLDBOOT_CPU2, - QCOM_SCM_FLAG_COLDBOOT_CPU3, - }; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - if (!cpus || (cpus && cpumask_empty(cpus))) - return -EINVAL; - - for_each_cpu(cpu, cpus) { - if (cpu < ARRAY_SIZE(scm_cb_flags)) - flags |= scm_cb_flags[cpu]; - else - set_cpu_present(cpu, false); - } - - desc.args[0] = flags; - desc.args[1] = virt_to_phys(entry); - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 - -struct qcom_scm_entry { - int flag; - void *entry; -}; - -static struct qcom_scm_entry qcom_scm_wb[] = { - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, - { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, -}; - -/** - * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus - * @dev: Device pointer - * @entry: Entry point function for the cpus - * @cpus: The cpumask of cpus that will use the entry point - * - * Set the Linux entry point for the SCM to transfer control to when coming - * out of a power down. CPU power down may be executed on cpuidle or hotplug. - */ -int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, - const cpumask_t *cpus) -{ - int ret; - int flags = 0; - int cpu; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR, - }; - - /* - * Reassign only if we are switching from hotplug entry point - * to cpuidle entry point or vice versa. - */ - for_each_cpu(cpu, cpus) { - if (entry == qcom_scm_wb[cpu].entry) - continue; - flags |= qcom_scm_wb[cpu].flag; - } - - /* No change in entry function */ - if (!flags) - return 0; - - desc.args[0] = flags; - desc.args[1] = virt_to_phys(entry); - ret = qcom_scm_call(dev, &desc); - if (!ret) { - for_each_cpu(cpu, cpus) - qcom_scm_wb[cpu].entry = entry; - } - - return ret; -} - -/** - * qcom_scm_cpu_power_down() - Power down the cpu - * @flags - Flags to flush cache - * - * This is an end point to power down cpu. If there was a pending interrupt, - * the control would return from this function, otherwise, the cpu jumps to the - * warm boot entry point set for this cpu upon reset. - */ -void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_TERMINATE_PC, - .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, - .arginfo = QCOM_SCM_ARGS(1), - .owner = ARM_SMCCC_OWNER_SIP, - }; - - qcom_scm_call_atomic(dev, &desc); -} - -int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = state; - desc.args[1] = id; - desc.arginfo = QCOM_SCM_ARGS(2); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_set_dload_mode(struct device *dev, bool enable) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; - desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - return ret ? false : !!desc.res[0]; -} - -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, - dma_addr_t metadata_phys) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.args[1] = metadata_phys; - desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, - phys_addr_t addr, phys_addr_t size) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.args[1] = addr; - desc.args[2] = size; - desc.arginfo = QCOM_SCM_ARGS(3); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = peripheral; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = reset; - desc.args[1] = 0; - desc.arginfo = QCOM_SCM_ARGS(2); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, - unsigned int *val) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_READ, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = addr; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call_atomic(dev, &desc); - if (ret >= 0) - *val = desc.res[0]; - - return ret < 0 ? ret : 0; -} - -int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_WRITE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = addr; - desc.args[1] = val; - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -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, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.arginfo = QCOM_SCM_ARGS(1); - switch (qcom_smc_convention) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - desc.args[0] = SMCCC_FUNCNUM(svc_id, cmd_id) | - (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); - break; - case SMC_CONVENTION_LEGACY: - desc.args[0] = LEGACY_FUNCNUM(svc_id, cmd_id); - break; - default: - pr_err("Unknown SMC convention being used\n"); - return -EINVAL; - } - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = device_id; - desc.args[1] = spare; - desc.arginfo = QCOM_SCM_ARGS(2); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, - size_t *size) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = spare; - desc.arginfo = QCOM_SCM_ARGS(1); - - ret = qcom_scm_call(dev, &desc); - - if (size) - *size = desc.res[0]; - - return ret ? : desc.res[1]; -} - -int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, - u32 spare) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - desc.args[0] = addr; - desc.args[1] = size; - desc.args[2] = spare; - desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, - QCOM_SCM_VAL); - - ret = qcom_scm_call(dev, &desc); - - /* the pg table has been initialized already, ignore the error */ - if (ret == -EPERM) - ret = 0; - - return ret; -} - -int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, - size_t mem_sz, phys_addr_t src, size_t src_sz, - phys_addr_t dest, size_t dest_sz) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_ASSIGN, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = mem_region; - desc.args[1] = mem_sz; - desc.args[2] = src; - desc.args[3] = src_sz; - desc.args[4] = dest; - desc.args[5] = dest_sz; - desc.args[6] = 0; - - desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, - QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, - QCOM_SCM_VAL, QCOM_SCM_VAL); - - ret = qcom_scm_call(dev, &desc); - - return ret ? : desc.res[0]; -} - -int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, - u32 req_cnt, u32 *resp) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_HDCP, - .cmd = QCOM_SCM_HDCP_INVOKE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) - return -ERANGE; - - desc.args[0] = req[0].addr; - desc.args[1] = req[0].val; - desc.args[2] = req[1].addr; - desc.args[3] = req[1].val; - desc.args[4] = req[2].addr; - desc.args[5] = req[2].val; - desc.args[6] = req[3].addr; - desc.args[7] = req[3].val; - desc.args[8] = req[4].addr; - desc.args[9] = req[4].val; - desc.arginfo = QCOM_SCM_ARGS(10); - - ret = qcom_scm_call(dev, &desc); - *resp = desc.res[0]; - - return ret; -} - -int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_SMMU_PROGRAM, - .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL; - desc.args[1] = en; - desc.arginfo = QCOM_SCM_ARGS(2); - - return qcom_scm_call_atomic(dev, &desc); -} - -void __qcom_scm_init(void) -{ - qcom_smc_convention = SMC_CONVENTION_LEGACY; - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) - goto out; - - qcom_smc_convention = SMC_CONVENTION_ARM_64; - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) - goto out; - - qcom_smc_convention = SMC_CONVENTION_ARM_32; - if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, - QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) - goto out; - - qcom_smc_convention = SMC_CONVENTION_UNKNOWN; -out: - pr_debug("QCOM SCM SMC Convention: %d\n", qcom_smc_convention); -} diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c new file mode 100644 index 0000000..355d9d7 --- /dev/null +++ b/drivers/firmware/qcom_scm-smc.c @@ -0,0 +1,949 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/qcom_scm.h> +#include <linux/arm-smccc.h> +#include <linux/dma-mapping.h> + +#include "qcom_scm.h" + +#define MAX_QCOM_SCM_ARGS 10 +#define MAX_QCOM_SCM_RETS 3 + +enum qcom_scm_arg_types { + QCOM_SCM_VAL, + QCOM_SCM_RO, + QCOM_SCM_RW, + QCOM_SCM_BUFVAL, +}; + +#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ + (((a) & 0x3) << 4) | \ + (((b) & 0x3) << 6) | \ + (((c) & 0x3) << 8) | \ + (((d) & 0x3) << 10) | \ + (((e) & 0x3) << 12) | \ + (((f) & 0x3) << 14) | \ + (((g) & 0x3) << 16) | \ + (((h) & 0x3) << 18) | \ + (((i) & 0x3) << 20) | \ + (((j) & 0x3) << 22) | \ + ((num) & 0xf)) + +#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +/** + * struct qcom_scm_desc + * @arginfo: Metadata describing the arguments in args[] + * @args: The array of arguments for the secure syscall + * @res: The values returned by the secure syscall + */ +struct qcom_scm_desc { + u32 svc; + u32 cmd; + u32 arginfo; + u64 args[MAX_QCOM_SCM_ARGS]; + u64 res[MAX_QCOM_SCM_RETS]; + u32 owner; +}; + +struct arm_smccc_args { + unsigned long a[8]; +}; + +enum qcom_smc_convention { + SMC_CONVENTION_UNKNOWN, + SMC_CONVENTION_LEGACY, + SMC_CONVENTION_ARM_32, + SMC_CONVENTION_ARM_64, +}; + +static enum qcom_smc_convention qcom_smc_convention = SMC_CONVENTION_UNKNOWN; +static DEFINE_MUTEX(qcom_scm_lock); + +#define QCOM_SCM_EBUSY_WAIT_MS 30 +#define QCOM_SCM_EBUSY_MAX_RETRY 20 + +#define SMCCC_FUNCNUM(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) +#define SMCCC_N_REG_ARGS 4 +#define SMCCC_FIRST_REG_IDX 2 +#define SMCCC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SMCCC_N_REG_ARGS + 1) +#define SMCCC_LAST_REG_IDX (SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1) + +static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc, + struct arm_smccc_res *res) +{ + unsigned long a0 = smc->a[0]; + struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; + + quirk.state.a6 = 0; + + do { + arm_smccc_smc_quirk(a0, smc->a[1], smc->a[2], smc->a[3], + smc->a[4], smc->a[5], quirk.state.a6, + smc->a[7], res, &quirk); + + if (res->a0 == QCOM_SCM_INTERRUPTED) + a0 = res->a0; + + } while (res->a0 == QCOM_SCM_INTERRUPTED); +} + +static int qcom_scm_call_smccc(struct device *dev, + struct qcom_scm_desc *desc, bool atomic) +{ + int arglen = desc->arginfo & 0xf; + int i; + dma_addr_t args_phys = 0; + void *args_virt = NULL; + size_t alloc_len; + gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; + struct arm_smccc_res res; + struct arm_smccc_args smc = {0}; + + smc.a[0] = ARM_SMCCC_CALL_VAL( + atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL, + (qcom_smc_convention == SMC_CONVENTION_ARM_64) ? + ARM_SMCCC_SMC_64 : + ARM_SMCCC_SMC_32, + desc->owner, + SMCCC_FUNCNUM(desc->svc, desc->cmd)); + smc.a[1] = desc->arginfo; + for (i = 0; i < SMCCC_N_REG_ARGS; i++) + smc.a[i + SMCCC_FIRST_REG_IDX] = desc->args[i]; + + if (unlikely(arglen > SMCCC_N_REG_ARGS)) { + alloc_len = SMCCC_N_EXT_ARGS * sizeof(u64); + args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); + + if (!args_virt) + return -ENOMEM; + + if (qcom_smc_convention == SMC_CONVENTION_ARM_32) { + __le32 *args = args_virt; + + for (i = 0; i < SMCCC_N_EXT_ARGS; i++) + args[i] = cpu_to_le32(desc->args[i + + SMCCC_N_REG_ARGS - 1]); + } else { + __le64 *args = args_virt; + + for (i = 0; i < SMCCC_N_EXT_ARGS; i++) + args[i] = cpu_to_le64(desc->args[i + + SMCCC_N_REG_ARGS - 1]); + } + + args_phys = dma_map_single(dev, args_virt, alloc_len, + DMA_TO_DEVICE); + + if (dma_mapping_error(dev, args_phys)) { + kfree(args_virt); + return -ENOMEM; + } + + smc.a[SMCCC_LAST_REG_IDX] = args_phys; + } + + if (atomic) { + __qcom_scm_call_do_quirk(&smc, &res); + } else { + int retry_count = 0; + + do { + mutex_lock(&qcom_scm_lock); + + __qcom_scm_call_do_quirk(&smc, &res); + + mutex_unlock(&qcom_scm_lock); + + if (res.a0 == QCOM_SCM_V2_EBUSY) { + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) + break; + msleep(QCOM_SCM_EBUSY_WAIT_MS); + } + } while (res.a0 == QCOM_SCM_V2_EBUSY); + } + + if (args_virt) { + dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); + kfree(args_virt); + } + + desc->res[0] = res.a1; + desc->res[1] = res.a2; + desc->res[2] = res.a3; + + if (res.a0 < 0) + return qcom_scm_remap_error(res.a0); + + return 0; +} + +#define LEGACY_FUNCNUM(s, c) (((s) << 10) | ((c) & 0x3ff)) + +/** + * struct 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 legacy_get_command_buffer() + * + * An SCM command is laid out in memory as follows: + * + * ------------------- <--- struct legacy_command + * | command header | + * ------------------- <--- legacy_get_command_buffer() + * | command buffer | + * ------------------- <--- struct legacy_response and + * | response header | legacy_command_to_response() + * ------------------- <--- legacy_get_response_buffer() + * | response buffer | + * ------------------- + * + * There can be arbitrary padding between the headers and buffers so + * you should always use the appropriate qcom_scm_get_*_buffer() routines + * to access the buffers in a safe manner. + */ +struct legacy_command { + __le32 len; + __le32 buf_offset; + __le32 resp_hdr_offset; + __le32 id; + __le32 buf[0]; +}; + +/** + * struct legacy_response - one SCM response buffer + * @len: total available memory for response + * @buf_offset: start of response data relative to start of legacy_response + * @is_complete: indicates if the command has finished processing + */ +struct legacy_response { + __le32 len; + __le32 buf_offset; + __le32 is_complete; +}; + +/** + * legacy_command_to_response() - Get a pointer to a legacy_response + * @cmd: command + * + * Returns a pointer to a response for a command. + */ +static inline struct legacy_response *legacy_command_to_response( + const struct legacy_command *cmd) +{ + return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); +} + +/** + * 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 *legacy_get_command_buffer(const struct legacy_command *cmd) +{ + return (void *)cmd->buf; +} + +/** + * 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 *legacy_get_response_buffer(const struct legacy_response *rsp) +{ + return (void *)rsp + le32_to_cpu(rsp->buf_offset); +} + +static void __qcom_scm_call_do(const struct arm_smccc_args *smc, + struct arm_smccc_res *res) +{ + do { + arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3], + smc->a[4], smc->a[5], smc->a[6], smc->a[7], res); + } while (res->a0 == QCOM_SCM_INTERRUPTED); +} + +/** + * qcom_scm_call_legacy() - Send an SCM command + * @dev: struct device + * @svc_id: service identifier + * @cmd_id: command identifier + * @cmd_buf: command buffer + * @cmd_len: length of the command buffer + * @resp_buf: response buffer + * @resp_len: length of the response buffer + * + * 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. + */ +static int qcom_scm_call_legacy(struct device *dev, struct qcom_scm_desc *desc) +{ + int arglen = desc->arginfo & 0xf; + int ret = 0, context_id; + size_t i; + struct legacy_command *cmd; + struct legacy_response *rsp; + struct arm_smccc_args smc = {0}; + struct arm_smccc_res 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; + __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(LEGACY_FUNCNUM(desc->svc, desc->cmd)); + + arg_buf = legacy_get_command_buffer(cmd); + for (i = 0; i < arglen; i++) + arg_buf[i] = cpu_to_le32(desc->args[i]); + + rsp = 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.a[0] = 1; + smc.a[1] = (unsigned long)&context_id; + smc.a[2] = cmd_phys; + + mutex_lock(&qcom_scm_lock); + __qcom_scm_call_do(&smc, &res); + if (res.a0 < 0) + ret = qcom_scm_remap_error(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); + + res_buf = legacy_get_response_buffer(rsp); + for (i = 0; i < MAX_QCOM_SCM_RETS; i++) + desc->res[i] = le32_to_cpu(res_buf[i]); +out: + dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); + kfree(cmd); + return ret; +} + +#define LEGACY_ATOMIC_N_REG_ARGS 5 +#define LEGACY_ATOMIC_FIRST_REG_IDX 2 +#define LEGACY_CLASS_REGISTER (0x2 << 8) +#define LEGACY_MASK_IRQS BIT(5) +#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \ + LEGACY_CLASS_REGISTER | \ + LEGACY_MASK_IRQS | \ + (n & 0xf)) + +/** + * qcom_scm_call_atomic_legacy() - Send an atomic SCM command with up to + * 5 arguments and 3 return values + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +static int qcom_scm_call_atomic_legacy(struct device *dev, + struct qcom_scm_desc *desc) +{ + int context_id; + struct arm_smccc_args smc = {0}; + struct arm_smccc_res res; + size_t i, arglen = desc->arginfo & 0xf; + + BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS); + + smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen); + smc.a[1] = (unsigned long)&context_id; + + for (i = 0; i < arglen; i++) + smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i]; + + arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3], + smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res); + + desc->res[0] = res.a1; + desc->res[1] = res.a2; + desc->res[2] = res.a3; + + return res.a0; +} + +/** + * qcom_scm_call() - Invoke a syscall in the secure world + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. + */ +static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc) +{ + might_sleep(); + switch (qcom_smc_convention) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + return qcom_scm_call_smccc(dev, desc, false); + case SMC_CONVENTION_LEGACY: + return qcom_scm_call_legacy(dev, desc); + default: + pr_err("Unknown current SCM calling convention.\n"); + return -EINVAL; + } +} + +/** + * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * @res: Structure containing results from SMC/HVC call + * + * Sends a command to the SCM and waits for the command to finish processing. + * This can be called in atomic context. + */ +static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc) +{ + switch (qcom_smc_convention) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + return qcom_scm_call_smccc(dev, desc, true); + case SMC_CONVENTION_LEGACY: + return qcom_scm_call_atomic_legacy(dev, desc); + default: + pr_err("Unknown current SCM calling convention.\n"); + return -EINVAL; + } +} + +#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the cold boot address of the cpus. Any cpu outside the supported + * range would be removed from the cpu present mask. + */ +int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus) +{ + int flags = 0; + int cpu; + int scm_cb_flags[] = { + QCOM_SCM_FLAG_COLDBOOT_CPU0, + QCOM_SCM_FLAG_COLDBOOT_CPU1, + QCOM_SCM_FLAG_COLDBOOT_CPU2, + QCOM_SCM_FLAG_COLDBOOT_CPU3, + }; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_ADDR, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + if (!cpus || (cpus && cpumask_empty(cpus))) + return -EINVAL; + + for_each_cpu(cpu, cpus) { + if (cpu < ARRAY_SIZE(scm_cb_flags)) + flags |= scm_cb_flags[cpu]; + else + set_cpu_present(cpu, false); + } + + desc.args[0] = flags; + desc.args[1] = virt_to_phys(entry); + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call_atomic(dev, &desc); +} + +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 + +struct qcom_scm_entry { + int flag; + void *entry; +}; + +static struct qcom_scm_entry qcom_scm_wb[] = { + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, +}; + +/** + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @dev: Device pointer + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the Linux entry point for the SCM to transfer control to when coming + * out of a power down. CPU power down may be executed on cpuidle or hotplug. + */ +int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus) +{ + int ret; + int flags = 0; + int cpu; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_ADDR, + }; + + /* + * Reassign only if we are switching from hotplug entry point + * to cpuidle entry point or vice versa. + */ + for_each_cpu(cpu, cpus) { + if (entry == qcom_scm_wb[cpu].entry) + continue; + flags |= qcom_scm_wb[cpu].flag; + } + + /* No change in entry function */ + if (!flags) + return 0; + + desc.args[0] = flags; + desc.args[1] = virt_to_phys(entry); + ret = qcom_scm_call(dev, &desc); + if (!ret) { + for_each_cpu(cpu, cpus) + qcom_scm_wb[cpu].entry = entry; + } + + return ret; +} + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags - Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void __qcom_scm_cpu_power_down(struct device *dev, u32 flags) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_TERMINATE_PC, + .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, + .arginfo = QCOM_SCM_ARGS(1), + .owner = ARM_SMCCC_OWNER_SIP, + }; + + qcom_scm_call_atomic(dev, &desc); +} + +int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = state; + desc.args[1] = id; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_set_dload_mode(struct device *dev, bool enable) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE; + desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call_atomic(dev, &desc); +} + +bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, &desc); + + return ret ? false : !!desc.res[0]; +} + +int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, + dma_addr_t metadata_phys) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = peripheral; + desc.args[1] = metadata_phys; + desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, + phys_addr_t addr, phys_addr_t size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = peripheral; + desc.args[1] = addr; + desc.args[2] = size; + desc.arginfo = QCOM_SCM_ARGS(3); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = reset; + desc.args[1] = 0; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, + unsigned int *val) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_IO, + .cmd = QCOM_SCM_IO_READ, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = addr; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call_atomic(dev, &desc); + if (ret >= 0) + *val = desc.res[0]; + + return ret < 0 ? ret : 0; +} + +int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_IO, + .cmd = QCOM_SCM_IO_WRITE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = addr; + desc.args[1] = val; + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call_atomic(dev, &desc); +} + +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, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.arginfo = QCOM_SCM_ARGS(1); + switch (qcom_smc_convention) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + desc.args[0] = SMCCC_FUNCNUM(svc_id, cmd_id) | + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); + break; + case SMC_CONVENTION_LEGACY: + desc.args[0] = LEGACY_FUNCNUM(svc_id, cmd_id); + break; + default: + pr_err("Unknown SMC convention being used\n"); + return -EINVAL; + } + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = device_id; + desc.args[1] = spare; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, + size_t *size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = spare; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, &desc); + + if (size) + *size = desc.res[0]; + + return ret ? : desc.res[1]; +} + +int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, + u32 spare) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + desc.args[0] = addr; + desc.args[1] = size; + desc.args[2] = spare; + desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, + QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, &desc); + + /* the pg table has been initialized already, ignore the error */ + if (ret == -EPERM) + ret = 0; + + return ret; +} + +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_ASSIGN, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = mem_region; + desc.args[1] = mem_sz; + desc.args[2] = src; + desc.args[3] = src_sz; + desc.args[4] = dest; + desc.args[5] = dest_sz; + desc.args[6] = 0; + + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, &desc); + + return ret ? : desc.res[0]; +} + +int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, + u32 req_cnt, u32 *resp) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_HDCP, + .cmd = QCOM_SCM_HDCP_INVOKE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) + return -ERANGE; + + desc.args[0] = req[0].addr; + desc.args[1] = req[0].val; + desc.args[2] = req[1].addr; + desc.args[3] = req[1].val; + desc.args[4] = req[2].addr; + desc.args[5] = req[2].val; + desc.args[6] = req[3].addr; + desc.args[7] = req[3].val; + desc.args[8] = req[4].addr; + desc.args[9] = req[4].val; + desc.arginfo = QCOM_SCM_ARGS(10); + + ret = qcom_scm_call(dev, &desc); + *resp = desc.res[0]; + + return ret; +} + +int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_SMMU_PROGRAM, + .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL; + desc.args[1] = en; + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call_atomic(dev, &desc); +} + +void __qcom_scm_init(void) +{ + qcom_smc_convention = SMC_CONVENTION_LEGACY; + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) + goto out; + + qcom_smc_convention = SMC_CONVENTION_ARM_64; + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) + goto out; + + qcom_smc_convention = SMC_CONVENTION_ARM_32; + if (__qcom_scm_is_call_available(NULL, QCOM_SCM_SVC_INFO, + QCOM_SCM_INFO_IS_CALL_AVAIL) == 1) + goto out; + + qcom_smc_convention = SMC_CONVENTION_UNKNOWN; +out: + pr_debug("QCOM SCM SMC Convention: %d\n", qcom_smc_convention); +}
- Rename qcom_scm-64.c to qcom_scm-smc as it supports SMC-based calling conventions for 32 and 64-bit targets - Remove qcom_scm-32.c as it has been superceded by qcom_scm-smc.c - Update Kconfig to select QCOM_SCM_TRANSPORT_SMC on ARM and ARM64 targets Signed-off-by: Elliot Berman <eberman@codeaurora.org> --- drivers/firmware/Kconfig | 18 +- drivers/firmware/Makefile | 4 +- drivers/firmware/qcom_scm-32.c | 660 ---------------------------- drivers/firmware/qcom_scm-64.c | 949 ---------------------------------------- drivers/firmware/qcom_scm-smc.c | 949 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 961 insertions(+), 1619 deletions(-) delete mode 100644 drivers/firmware/qcom_scm-32.c delete mode 100644 drivers/firmware/qcom_scm-64.c create mode 100644 drivers/firmware/qcom_scm-smc.c