7769d7fea8
Add TLB conflict fault handler and try to handle gracefully. Check whether TLB fault can be handled by EL2 and cause panic if EL2 is not able to handle. Change-Id: I276ec5413411932bd8a67ed4c85ebbf66f4affcf Signed-off-by: Runmin Wang <runminw@codeaurora.org> Signed-off-by: Prasad Sodagudi <psodagud@codeaurora.org> Signed-off-by: Naina Mehta <nainmeht@codeaurora.org>
2611 lines
60 KiB
C
2611 lines
60 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2015,2020-2021 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 <asm/cacheflush.h>
|
|
|
|
#include <linux/qtee_shmbridge.h>
|
|
#include <soc/qcom/qseecom_scm.h>
|
|
#include <soc/qcom/qseecomi.h>
|
|
|
|
#include "qcom_scm.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/scm.h>
|
|
|
|
#include <linux/habmm.h>
|
|
|
|
#define MAX_QCOM_SCM_ARGS 10
|
|
#define MAX_QCOM_SCM_RETS 3
|
|
|
|
#define QCOM_SCM_ATOMIC BIT(0)
|
|
#define QCOM_SCM_NORETRY BIT(1)
|
|
|
|
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);
|
|
static bool has_queried;
|
|
static DEFINE_SPINLOCK(query_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_EXT_IDX (SMCCC_N_REG_ARGS - 1)
|
|
#define SMCCC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SMCCC_N_REG_ARGS + 1)
|
|
#define SMCCC_FIRST_REG_IDX 2
|
|
#define SMCCC_LAST_REG_IDX (SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1)
|
|
|
|
#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;
|
|
};
|
|
|
|
#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))
|
|
|
|
#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 },
|
|
};
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_QCOM_SCM_QCPE)
|
|
|
|
#ifdef CONFIG_GHS_VMM
|
|
struct scm_extra_arg {
|
|
union {
|
|
u32 args32[N_EXT_SCM_ARGS];
|
|
u64 args64[N_EXT_SCM_ARGS];
|
|
};
|
|
};
|
|
#endif
|
|
|
|
struct smc_params_s {
|
|
uint64_t fn_id;
|
|
uint64_t arginfo;
|
|
uint64_t args[MAX_SCM_ARGS];
|
|
} __packed;
|
|
|
|
static u32 handle;
|
|
static bool opened;
|
|
|
|
static int scm_qcpe_hab_open(void)
|
|
{
|
|
int ret;
|
|
|
|
if (!opened) {
|
|
ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0);
|
|
if (ret) {
|
|
pr_err("habmm_socket_open failed with ret = %d\n", ret);
|
|
return ret;
|
|
}
|
|
opened = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void scm_qcpe_hab_close(void)
|
|
{
|
|
if (opened) {
|
|
habmm_socket_close(handle);
|
|
opened = false;
|
|
handle = 0;
|
|
}
|
|
}
|
|
|
|
/* Send SMC over HAB, receive the response. Both operations are blocking. */
|
|
/* This is meant to be called from non-atomic context. */
|
|
static int scm_qcpe_hab_send_receive(struct smc_params_s *smc_params,
|
|
u32 *size_bytes)
|
|
{
|
|
int ret;
|
|
|
|
ret = habmm_socket_send(handle, smc_params, sizeof(*smc_params), 0);
|
|
if (ret) {
|
|
pr_err("habmm_socket_send failed, ret= 0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
memset(smc_params, 0x0, sizeof(*smc_params));
|
|
|
|
do {
|
|
*size_bytes = sizeof(*smc_params);
|
|
ret = habmm_socket_recv(handle, smc_params, size_bytes, 0,
|
|
HABMM_SOCKET_RECV_FLAGS_UNINTERRUPTIBLE);
|
|
} while (-EINTR == ret);
|
|
|
|
if (ret) {
|
|
pr_err("habmm_socket_recv failed, ret= 0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Send SMC over HAB, receive the response, in non-blocking mode. */
|
|
/* This is meant to be called from atomic context. */
|
|
static int scm_qcpe_hab_send_receive_atomic(struct smc_params_s *smc_params,
|
|
u32 *size_bytes)
|
|
{
|
|
int ret;
|
|
unsigned long delay;
|
|
|
|
delay = jiffies + (HZ); /* 1 second delay for send */
|
|
|
|
do {
|
|
ret = habmm_socket_send(handle,
|
|
smc_params, sizeof(*smc_params),
|
|
HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING);
|
|
} while ((-EAGAIN == ret) && time_before(jiffies, delay));
|
|
|
|
if (ret) {
|
|
pr_err("HAB send failed, non-blocking, ret= 0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
memset(smc_params, 0x0, sizeof(*smc_params));
|
|
|
|
delay = jiffies + (HZ); /* 1 second delay for receive */
|
|
|
|
do {
|
|
*size_bytes = sizeof(*smc_params);
|
|
ret = habmm_socket_recv(handle, smc_params, size_bytes, 0,
|
|
HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING);
|
|
} while ((-EAGAIN == ret) && time_before(jiffies, delay) &&
|
|
(*size_bytes == 0));
|
|
|
|
if (ret) {
|
|
pr_err("HAB recv failed, non-blocking, ret= 0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int scm_call_qcpe(const struct arm_smccc_args *smc,
|
|
struct arm_smccc_res *res, const bool atomic)
|
|
{
|
|
u32 size_bytes;
|
|
struct smc_params_s smc_params = {0,};
|
|
int ret;
|
|
#ifdef CONFIG_GHS_VMM
|
|
int i;
|
|
uint64_t arglen = smc->a[1] & 0xf;
|
|
struct ion_handle *ihandle = NULL;
|
|
#endif
|
|
|
|
pr_info("SCM IN [QCPE]: 0x%x, 0x%x, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
|
|
smc->a[0], smc->a[1], smc->a[2], smc->a[3], smc->a[4], smc->a[5],
|
|
smc->a[5]);
|
|
|
|
if (!opened) {
|
|
if (!atomic) {
|
|
if (scm_qcpe_hab_open()) {
|
|
pr_err("HAB channel re-open failed\n");
|
|
return -ENODEV;
|
|
}
|
|
} else {
|
|
pr_err("HAB channel is not opened\n");
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
smc_params.fn_id = smc->a[0];
|
|
smc_params.arginfo = smc->a[1];
|
|
smc_params.args[0] = smc->a[2];
|
|
smc_params.args[1] = smc->a[3];
|
|
smc_params.args[2] = smc->a[4];
|
|
|
|
#ifdef CONFIG_GHS_VMM
|
|
if (arglen <= N_REGISTER_ARGS) {
|
|
smc_params.args[FIRST_EXT_ARG_IDX] = smc->a[5];
|
|
} else {
|
|
struct scm_extra_arg *argbuf =
|
|
(struct scm_extra_arg *)desc->extra_arg_buf;
|
|
int j = 0;
|
|
|
|
if (scm_version == SMC_CONVENTION_ARM_64)
|
|
for (i = FIRST_EXT_ARG_IDX; i < MAX_QCOM_SCM_ARGS; i++)
|
|
smc_params.args[i] = argbuf->args64[j++];
|
|
else
|
|
for (i = FIRST_EXT_ARG_IDX; i < MAX_QCOM_SCM_ARGS; i++)
|
|
smc_params.args[i] = argbuf->args32[j++];
|
|
}
|
|
|
|
ret = ionize_buffers(smc->a[0] & (~SMC64_MASK), &smc_params, &ihandle);
|
|
if (ret)
|
|
return ret;
|
|
#else
|
|
smc_params.args[3] = smc->a[5];
|
|
smc_params.args[4] = 0;
|
|
#endif
|
|
|
|
if (!atomic) {
|
|
ret = scm_qcpe_hab_send_receive(&smc_params, &size_bytes);
|
|
if (ret) {
|
|
pr_err("send/receive failed, non-atomic, ret= 0x%x\n",
|
|
ret);
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = scm_qcpe_hab_send_receive_atomic(&smc_params,
|
|
&size_bytes);
|
|
if (ret) {
|
|
pr_err("send/receive failed, ret= 0x%x\n", ret);
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
if (size_bytes != sizeof(smc_params)) {
|
|
pr_err("habmm_socket_recv expected size: %lu, actual=%u\n",
|
|
sizeof(smc_params),
|
|
size_bytes);
|
|
ret = QCOM_SCM_ERROR;
|
|
goto err_ret;
|
|
}
|
|
|
|
res->a1 = smc_params.args[1];
|
|
res->a2 = smc_params.args[2];
|
|
res->a3 = smc_params.args[3];
|
|
res->a0 = smc_params.args[0];
|
|
pr_info("SCM OUT [QCPE]: 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
|
|
res->a0, res->a1, res->a2, res->a3);
|
|
goto no_err;
|
|
|
|
err_ret:
|
|
if (!atomic) {
|
|
/* In case of an error, try to recover the hab connection
|
|
* for next time. This can only be done if called in
|
|
* non-atomic context.
|
|
*/
|
|
scm_qcpe_hab_close();
|
|
if (scm_qcpe_hab_open())
|
|
pr_err("scm_qcpe_hab_open failed\n");
|
|
}
|
|
|
|
no_err:
|
|
#ifdef CONFIG_GHS_VMM
|
|
if (ihandle)
|
|
free_ion_buffers(ihandle);
|
|
#endif
|
|
return res->a0;
|
|
}
|
|
|
|
#endif /* CONFIG_QCOM_SCM_QCPE */
|
|
|
|
static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
|
|
struct arm_smccc_res *res,
|
|
const bool atomic)
|
|
{
|
|
ktime_t time;
|
|
const bool trace = trace_scm_call_enabled();
|
|
#if !(IS_ENABLED(CONFIG_QCOM_SCM_QCPE))
|
|
unsigned long a0 = smc->a[0];
|
|
struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
|
|
|
|
quirk.state.a6 = 0;
|
|
#endif
|
|
if (trace)
|
|
time = ktime_get();
|
|
|
|
#if IS_ENABLED(CONFIG_QCOM_SCM_QCPE)
|
|
scm_call_qcpe(smc, res, atomic);
|
|
#else
|
|
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);
|
|
#endif
|
|
if (trace)
|
|
trace_scm_call(smc->a, res, ktime_us_delta(ktime_get(), time));
|
|
}
|
|
|
|
static int qcom_scm_call_smccc(struct device *dev,
|
|
struct qcom_scm_desc *desc, const u32 options)
|
|
{
|
|
int arglen = desc->arginfo & 0xf;
|
|
int i, ret;
|
|
size_t alloc_len;
|
|
const bool atomic = options & QCOM_SCM_ATOMIC;
|
|
gfp_t flag = atomic ? GFP_ATOMIC : GFP_NOIO;
|
|
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
|
|
u32 qcom_smccc_convention =
|
|
(qcom_smc_convention == SMC_CONVENTION_ARM_32) ?
|
|
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
|
|
struct arm_smccc_res res;
|
|
struct arm_smccc_args smc = {{0}};
|
|
struct qtee_shm shm = {0};
|
|
bool use_qtee_shmbridge;
|
|
|
|
smc.a[0] = ARM_SMCCC_CALL_VAL(
|
|
smccc_call_type,
|
|
qcom_smccc_convention,
|
|
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)) {
|
|
if (!dev)
|
|
return -EPROBE_DEFER;
|
|
|
|
alloc_len = SMCCC_N_EXT_ARGS * sizeof(u64);
|
|
use_qtee_shmbridge = qtee_shmbridge_is_enabled();
|
|
if (use_qtee_shmbridge) {
|
|
ret = qtee_shmbridge_allocate_shm(alloc_len, &shm);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
shm.vaddr = kzalloc(alloc_len, flag);
|
|
if (!shm.vaddr)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (qcom_smc_convention == SMC_CONVENTION_ARM_32) {
|
|
__le32 *args = shm.vaddr;
|
|
|
|
for (i = 0; i < SMCCC_N_EXT_ARGS; i++)
|
|
args[i] = cpu_to_le32(desc->args[i +
|
|
SMCCC_FIRST_EXT_IDX]);
|
|
} else {
|
|
__le64 *args = shm.vaddr;
|
|
|
|
for (i = 0; i < SMCCC_N_EXT_ARGS; i++)
|
|
args[i] = cpu_to_le64(desc->args[i +
|
|
SMCCC_FIRST_EXT_IDX]);
|
|
}
|
|
|
|
shm.paddr = dma_map_single(dev, shm.vaddr, alloc_len,
|
|
DMA_TO_DEVICE);
|
|
|
|
if (dma_mapping_error(dev, shm.paddr)) {
|
|
if (use_qtee_shmbridge)
|
|
qtee_shmbridge_free_shm(&shm);
|
|
else
|
|
kfree(shm.vaddr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
smc.a[SMCCC_LAST_REG_IDX] = shm.paddr;
|
|
}
|
|
|
|
if (atomic) {
|
|
__qcom_scm_call_do_quirk(&smc, &res, true);
|
|
} else {
|
|
int retry_count = 0;
|
|
|
|
do {
|
|
mutex_lock(&qcom_scm_lock);
|
|
__qcom_scm_call_do_quirk(&smc, &res, false);
|
|
mutex_unlock(&qcom_scm_lock);
|
|
|
|
if (res.a0 == QCOM_SCM_V2_EBUSY) {
|
|
if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY ||
|
|
(options & QCOM_SCM_NORETRY))
|
|
break;
|
|
msleep(QCOM_SCM_EBUSY_WAIT_MS);
|
|
}
|
|
} while (res.a0 == QCOM_SCM_V2_EBUSY);
|
|
}
|
|
|
|
if (unlikely(arglen > SMCCC_N_REG_ARGS)) {
|
|
dma_unmap_single(dev, shm.paddr, alloc_len,
|
|
DMA_TO_DEVICE);
|
|
if (use_qtee_shmbridge)
|
|
qtee_shmbridge_free_shm(&shm);
|
|
else
|
|
kfree(shm.vaddr);
|
|
}
|
|
|
|
desc->res[0] = res.a1;
|
|
desc->res[1] = res.a2;
|
|
desc->res[2] = res.a3;
|
|
|
|
return res.a0 ? qcom_scm_remap_error(res.a0) : 0;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
ktime_t time;
|
|
const bool trace = trace_scm_call_enabled();
|
|
|
|
if (trace)
|
|
time = ktime_get();
|
|
|
|
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);
|
|
|
|
if (trace)
|
|
trace_scm_call(smc->a, res, ktime_us_delta(ktime_get(), time));
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
if (!dev)
|
|
return -EPROBE_DEFER;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
const bool trace = trace_scm_call_enabled();
|
|
ktime_t time;
|
|
|
|
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];
|
|
|
|
if (trace)
|
|
time = ktime_get();
|
|
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);
|
|
|
|
if (trace)
|
|
trace_scm_call(smc.a, &res, ktime_us_delta(ktime_get(), time));
|
|
|
|
desc->res[0] = res.a1;
|
|
desc->res[1] = res.a2;
|
|
desc->res[2] = res.a3;
|
|
|
|
return res.a0;
|
|
}
|
|
|
|
static void __query_convention(void)
|
|
{
|
|
unsigned long flags;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_INFO,
|
|
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
|
|
.args[0] = SMCCC_FUNCNUM(QCOM_SCM_SVC_INFO,
|
|
QCOM_SCM_INFO_IS_CALL_AVAIL) |
|
|
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
|
|
.arginfo = QCOM_SCM_ARGS(1),
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
int ret;
|
|
|
|
spin_lock_irqsave(&query_lock, flags);
|
|
if (has_queried)
|
|
goto out;
|
|
|
|
qcom_smc_convention = SMC_CONVENTION_ARM_64;
|
|
ret = qcom_scm_call_smccc(NULL, &desc, true);
|
|
if (!ret && desc.res[0] == 1)
|
|
goto out;
|
|
|
|
qcom_smc_convention = SMC_CONVENTION_ARM_32;
|
|
ret = qcom_scm_call_smccc(NULL, &desc, true);
|
|
if (!ret && desc.res[0] == 1)
|
|
goto out;
|
|
|
|
qcom_smc_convention = SMC_CONVENTION_LEGACY;
|
|
out:
|
|
has_queried = true;
|
|
spin_unlock_irqrestore(&query_lock, flags);
|
|
pr_debug("QCOM SCM SMC Convention: %d\n", qcom_smc_convention);
|
|
}
|
|
|
|
static inline enum qcom_smc_convention __get_convention(void)
|
|
{
|
|
if (unlikely(!has_queried))
|
|
__query_convention();
|
|
return qcom_smc_convention;
|
|
}
|
|
|
|
/**
|
|
* 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 (__get_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 (__get_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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* qcom_scm_call_noretry() - 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_noretry(struct device *dev, struct qcom_scm_desc *desc)
|
|
{
|
|
might_sleep();
|
|
switch (__get_convention()) {
|
|
case SMC_CONVENTION_ARM_32:
|
|
case SMC_CONVENTION_ARM_64:
|
|
return qcom_scm_call_smccc(dev, desc, QCOM_SCM_NORETRY);
|
|
case SMC_CONVENTION_LEGACY:
|
|
return qcom_scm_call_legacy(dev, desc);
|
|
default:
|
|
pr_err("Unknown current SCM calling convention.\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* scm_set_boot_addr_mc - Set entry physical address for cpus
|
|
* @addr: 32bit physical address
|
|
* @aff0: Collective bitmask of the affinity-level-0 of the mpidr
|
|
* 1<<aff0_CPU0| 1<<aff0_CPU1....... | 1<<aff0_CPU32
|
|
* Supports maximum 32 cpus under any affinity level.
|
|
* @aff1: Collective bitmask of the affinity-level-1 of the mpidr
|
|
* @aff2: Collective bitmask of the affinity-level-2 of the mpidr
|
|
* @flags: Flag to differentiate between coldboot vs warmboot
|
|
*/
|
|
int __qcom_scm_set_warm_boot_addr_mc(struct device *dev, void *entry, u32 aff0,
|
|
u32 aff1, u32 aff2, u32 flags)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_SET_ADDR_MC,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = virt_to_phys(entry);
|
|
desc.args[1] = aff0;
|
|
desc.args[2] = aff1;
|
|
desc.args[3] = aff2;
|
|
desc.args[4] = ~0ULL;
|
|
desc.args[5] = flags;
|
|
desc.arginfo = QCOM_SCM_ARGS(6);
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
/*
|
|
* 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] = virt_to_phys(entry);
|
|
desc.args[1] = flags;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
ret = qcom_scm_call(dev, &desc);
|
|
if (!ret) {
|
|
for_each_cpu(cpu, cpus)
|
|
qcom_scm_wb[cpu].entry = entry;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __qcom_scm_cpu_hp(struct device *dev, u32 flags)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_TERMINATE_PC,
|
|
.args[0] = flags,
|
|
.arginfo = QCOM_SCM_ARGS(1),
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
qcom_scm_call_atomic(dev, &desc);
|
|
}
|
|
|
|
/**
|
|
* 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_sec_wdog_deactivate(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_SEC_WDOG_DIS,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 1;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_sec_wdog_trigger(struct device *dev)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_SEC_WDOG_TRIGGER,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
#ifdef CONFIG_TLB_CONF_HANDLER
|
|
int __qcom_scm_tlb_conf_handler(struct device *dev, unsigned long addr)
|
|
{
|
|
int ret;
|
|
|
|
#define SCM_TLB_CONFLICT_CMD 0x1F
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = SCM_TLB_CONFLICT_CMD,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = addr;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
#endif
|
|
|
|
void __qcom_scm_disable_sdi(struct device *dev)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_WDOG_DEBUG_PART,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 1;
|
|
desc.args[1] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
if (ret)
|
|
pr_err("Failed to disable secure wdog debug: %d\n", ret);
|
|
}
|
|
|
|
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_spin_cpu(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_SPIN_CPU,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_set_dload_mode(struct device *dev, enum qcom_download_mode mode)
|
|
{
|
|
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] = mode;
|
|
desc.args[1] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
return qcom_scm_call_atomic(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_config_cpu_errata(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_BOOT_CONFIG_CPU_ERRATA,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.arginfo = 0xffffffff;
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
void __qcom_scm_phy_update_scm_level_shifter(struct device *dev, u32 val)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_BOOT,
|
|
.cmd = QCOM_SCM_QUSB2PHY_LVL_SHIFTER_CMD_ID,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = val;
|
|
desc.args[1] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
if (ret)
|
|
pr_err("Failed to update scm level shifter=0x%x\n", ret);
|
|
}
|
|
|
|
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_get_sec_dump_state(struct device *dev, u32 *dump_state)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_UTIL,
|
|
.cmd = QCOM_SCM_UTIL_GET_SEC_DUMP_STATE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (dump_state)
|
|
*dump_state = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_tz_blsp_modify_owner(struct device *dev, int food, u64 subsystem,
|
|
int *out)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_TZ,
|
|
.cmd = QOCM_SCM_TZ_BLSP_MODIFY_OWNER,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = subsystem;
|
|
desc.args[1] = food;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (out)
|
|
*out = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_io_reset(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_IO,
|
|
.cmd = QCOM_SCM_IO_RESET,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
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 (__get_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_get_feat_version(struct device *dev, u64 feat_id, u64 *version)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_INFO,
|
|
.cmd = QCOM_SCM_INFO_GET_FEAT_VERSION_CMD,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = feat_id;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (version)
|
|
*version = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __qcom_scm_halt_spmi_pmic_arbiter(struct device *dev)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_PWR,
|
|
.cmd = QCOM_SCM_PWR_IO_DISABLE_PMIC_ARBITER,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
if (ret)
|
|
pr_err("Failed to halt_spmi_pmic_arbiter=0x%x\n", ret);
|
|
}
|
|
|
|
void __qcom_scm_deassert_ps_hold(struct device *dev)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_PWR,
|
|
.cmd = QCOM_SCM_PWR_IO_DEASSERT_PS_HOLD,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 0;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
if (ret)
|
|
pr_err("Failed to deassert_ps_hold=0x%x\n", ret);
|
|
}
|
|
|
|
void __qcom_scm_mmu_sync(struct device *dev, bool sync)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_PWR,
|
|
.cmd = QCOM_SCM_PWR_MMU_SYNC,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = sync;
|
|
desc.arginfo = QCOM_SCM_ARGS(1);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
|
|
if (ret)
|
|
pr_err("MMU sync with Hypervisor off %x\n", ret);
|
|
}
|
|
|
|
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_mem_protect_video(struct device *dev,
|
|
u32 cp_start, u32 cp_size,
|
|
u32 cp_nonpixel_start, u32 cp_nonpixel_size)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_MEM_PROTECT_VIDEO,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = cp_start;
|
|
desc.args[1] = cp_size;
|
|
desc.args[2] = cp_nonpixel_start;
|
|
desc.args[3] = cp_nonpixel_size;
|
|
desc.arginfo = QCOM_SCM_ARGS(4);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_mem_protect_region_id(struct device *dev, phys_addr_t paddr,
|
|
size_t size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_MEM_PROTECT_REGION_ID,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = paddr;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RO, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_mem_protect_lock_id2_flat(struct device *dev,
|
|
phys_addr_t list_addr, size_t list_size,
|
|
size_t chunk_size, size_t memory_usage,
|
|
int lock)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_MEM_PROTECT_LOCK_ID2_FLAT,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = list_addr;
|
|
desc.args[1] = list_size;
|
|
desc.args[2] = chunk_size;
|
|
desc.args[3] = memory_usage;
|
|
desc.args[4] = lock;
|
|
desc.args[5] = 0;
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(6, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_iommu_secure_map(struct device *dev, phys_addr_t sg_list_addr,
|
|
size_t num_sg, size_t sg_block_size, u64 sec_id,
|
|
int cbndx, unsigned long iova, size_t total_len)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_IOMMU_SECURE_MAP2_FLAT,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = sg_list_addr;
|
|
desc.args[1] = num_sg;
|
|
desc.args[2] = sg_block_size;
|
|
desc.args[3] = sec_id;
|
|
desc.args[4] = cbndx;
|
|
desc.args[5] = iova;
|
|
desc.args[6] = total_len;
|
|
desc.args[7] = 0;
|
|
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(8, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_iommu_secure_unmap(struct device *dev, u64 sec_id, int cbndx,
|
|
unsigned long iova, size_t total_len)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_IOMMU_SECURE_UNMAP2_FLAT,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = sec_id;
|
|
desc.args[1] = cbndx;
|
|
desc.args[2] = iova;
|
|
desc.args[3] = total_len;
|
|
desc.args[4] = QCOM_SCM_IOMMU_TLBINVAL_FLAG;
|
|
desc.arginfo = QCOM_SCM_ARGS(5);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
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_mem_protect_sd_ctrl(struct device *dev, u32 devid,
|
|
phys_addr_t mem_addr, u64 mem_size, u32 vmid)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_CMD_SD_CTRL,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = devid;
|
|
desc.args[1] = mem_addr;
|
|
desc.args[2] = mem_size;
|
|
desc.args[3] = vmid;
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL,
|
|
QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_kgsl_set_smmu_aperture(struct device *dev,
|
|
unsigned int num_context_bank)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_CP_SMMU_APERTURE_ID,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = 0xffff0000 | ((QCOM_SCM_CP_APERTURE_REG & 0xff) << 8) |
|
|
(num_context_bank & 0xff);
|
|
desc.args[1] = 0xffffffff;
|
|
desc.args[2] = 0xffffffff;
|
|
desc.args[3] = 0xffffffff;
|
|
desc.arginfo = QCOM_SCM_ARGS(4);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* The following shmbridge functions should be called before the SCM driver
|
|
* has been initialized. If not, there could be errors that might cause the
|
|
* system to crash.
|
|
*/
|
|
int __qcom_scm_enable_shm_bridge(struct device *dev)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MEMP_SHM_BRIDGE_ENABLE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_delete_shm_bridge(struct device *dev, u64 handle)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MEMP_SHM_BRIDGE_DELETE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = handle;
|
|
desc.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_create_shm_bridge(struct device *dev, u64 pfn_and_ns_perm_flags,
|
|
u64 ipfn_and_s_perm_flags, u64 size_and_flags,
|
|
u64 ns_vmids, u64 *handle)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MEMP_SHM_BRDIGE_CREATE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = pfn_and_ns_perm_flags;
|
|
desc.args[1] = ipfn_and_s_perm_flags;
|
|
desc.args[2] = size_and_flags;
|
|
desc.args[3] = ns_vmids;
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (handle)
|
|
*handle = desc.res[1];
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_smmu_prepare_atos_id(struct device *dev, u64 dev_id, int cb_num,
|
|
int operation)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_SMMU_PREPARE_ATOS_ID,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = dev_id;
|
|
desc.args[1] = cb_num;
|
|
desc.args[2] = operation;
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_mdf_assign_memory_to_subsys(struct device *dev, u64 start_addr,
|
|
u64 end_addr, phys_addr_t paddr, u64 size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_MP,
|
|
.cmd = QCOM_SCM_MP_MPU_LOCK_NS_REGION,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = start_addr;
|
|
desc.args[1] = end_addr;
|
|
desc.args[2] = paddr;
|
|
desc.args[3] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(4);
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
bool __qcom_scm_dcvs_core_available(struct device *dev)
|
|
{
|
|
return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_DCVS,
|
|
QCOM_SCM_DCVS_INIT) &&
|
|
__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_DCVS,
|
|
QCOM_SCM_DCVS_UPDATE) &&
|
|
__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_DCVS,
|
|
QCOM_SCM_DCVS_RESET);
|
|
}
|
|
|
|
bool __qcom_scm_dcvs_ca_available(struct device *dev)
|
|
{
|
|
return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_DCVS,
|
|
QCOM_SCM_DCVS_INIT_CA_V2) &&
|
|
__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_DCVS,
|
|
QCOM_SCM_DCVS_UPDATE_CA_V2);
|
|
}
|
|
|
|
int __qcom_scm_dcvs_reset(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_RESET,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_dcvs_init_v2(struct device *dev, phys_addr_t addr, size_t size,
|
|
int *version)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_INIT_V2,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = addr;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (ret >= 0)
|
|
*version = desc.res[0];
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_dcvs_init_ca_v2(struct device *dev, phys_addr_t addr,
|
|
size_t size)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_INIT_CA_V2,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = addr;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_dcvs_update(struct device *dev, int level, s64 total_time,
|
|
s64 busy_time)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_UPDATE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = level;
|
|
desc.args[1] = total_time;
|
|
desc.args[2] = busy_time;
|
|
desc.arginfo = QCOM_SCM_ARGS(3);
|
|
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_dcvs_update_v2(struct device *dev, int level, s64 total_time,
|
|
s64 busy_time)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_UPDATE_V2,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = level;
|
|
desc.args[1] = total_time;
|
|
desc.args[2] = busy_time;
|
|
desc.arginfo = QCOM_SCM_ARGS(3);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_dcvs_update_ca_v2(struct device *dev, int level, s64 total_time,
|
|
s64 busy_time, int context_count)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_DCVS,
|
|
.cmd = QCOM_SCM_DCVS_UPDATE_CA_V2,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = level;
|
|
desc.args[1] = total_time;
|
|
desc.args[2] = busy_time;
|
|
desc.args[3] = context_count;
|
|
desc.arginfo = QCOM_SCM_ARGS(4);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_config_set_ice_key(struct device *dev, uint32_t index,
|
|
phys_addr_t paddr, size_t size,
|
|
uint32_t cipher, unsigned int data_unit,
|
|
unsigned int food)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_ES,
|
|
.cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = index;
|
|
desc.args[1] = paddr;
|
|
desc.args[2] = size;
|
|
desc.args[3] = cipher;
|
|
desc.args[4] = data_unit;
|
|
desc.args[5] = food;
|
|
desc.arginfo = QCOM_SCM_ARGS(6, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
return qcom_scm_call_noretry(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_clear_ice_key(struct device *dev, uint32_t index,
|
|
unsigned int food)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_ES,
|
|
.cmd = QCOM_SCM_ES_CLEAR_ICE_KEY,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = index;
|
|
desc.args[1] = food;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
return qcom_scm_call_noretry(dev, &desc);
|
|
}
|
|
|
|
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_lmh_read_buf_size(struct device *dev, int *size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = QCOM_SCM_LMH_DEBUG_READ_BUF_SIZE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(0);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (size)
|
|
*size = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_lmh_limit_dcvsh(struct device *dev, phys_addr_t payload,
|
|
uint32_t payload_size, u64 limit_node, uint32_t node_id,
|
|
u64 version)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = QCOM_SCM_LMH_LIMIT_DCVSH,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = payload;
|
|
desc.args[1] = payload_size;
|
|
desc.args[2] = limit_node;
|
|
desc.args[3] = node_id;
|
|
desc.args[4] = version;
|
|
desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_lmh_debug_read(struct device *dev, phys_addr_t payload,
|
|
uint32_t size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = QCOM_SCM_LMH_DEBUG_READ,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = payload;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_lmh_debug_config_write(struct device *dev, u64 cmd_id,
|
|
phys_addr_t payload, int payload_size, uint32_t *buf,
|
|
int buf_size)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = cmd_id,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
if (buf_size < 3)
|
|
return -EINVAL;
|
|
|
|
desc.args[0] = payload;
|
|
desc.args[1] = payload_size;
|
|
desc.args[2] = buf[0];
|
|
desc.args[3] = buf[1];
|
|
desc.args[4] = buf[2];
|
|
desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_lmh_get_type(struct device *dev, phys_addr_t payload,
|
|
u64 payload_size, u64 debug_type, uint32_t get_from,
|
|
uint32_t *size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = QCOM_SCM_LMH_DEBUG_GET_TYPE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = payload;
|
|
desc.args[1] = payload_size;
|
|
desc.args[2] = debug_type;
|
|
desc.args[3] = get_from;
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (size)
|
|
*size = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_lmh_fetch_data(struct device *dev,
|
|
u32 node_id, u32 debug_type, uint32_t *peak, uint32_t *avg)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_LMH,
|
|
.cmd = QCOM_SCM_LMH_DEBUG_FETCH_DATA,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = node_id;
|
|
desc.args[1] = debug_type;
|
|
desc.arginfo = SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (peak)
|
|
*peak = desc.res[0];
|
|
if (avg)
|
|
*avg = desc.res[1];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_smmu_change_pgtbl_format(struct device *dev, u64 dev_id,
|
|
int cbndx)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
|
|
.cmd = QCOM_SCM_SMMU_CHANGE_PGTBL_FORMAT,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = dev_id;
|
|
desc.args[1] = cbndx;
|
|
desc.args[2] = 1; /* Enable */
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_VAL,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
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_SECURE_LUT,
|
|
.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);
|
|
}
|
|
|
|
int __qcom_scm_smmu_notify_secure_lut(struct device *dev, u64 dev_id,
|
|
bool secure)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
|
|
.cmd = QCOM_SCM_SMMU_SECURE_LUT,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = dev_id;
|
|
desc.args[1] = secure;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_qdss_invoke(struct device *dev, phys_addr_t addr, size_t size,
|
|
u64 *out)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_QDSS,
|
|
.cmd = QCOM_SCM_QDSS_INVOKE,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = addr;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RO, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
if (out)
|
|
*out = desc.res[1];
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_camera_protect_all(struct device *dev, uint32_t protect,
|
|
uint32_t param)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_CAMERA,
|
|
.cmd = QCOM_SCM_CAMERA_PROTECT_ALL,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = protect;
|
|
desc.args[1] = param;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_VAL);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_camera_protect_phy_lanes(struct device *dev, bool protect,
|
|
u64 regmask)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_CAMERA,
|
|
.cmd = QCOM_SCM_CAMERA_PROTECT_PHY_LANES,
|
|
.owner = ARM_SMCCC_OWNER_SIP
|
|
};
|
|
|
|
desc.args[0] = protect;
|
|
desc.args[1] = regmask;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_tsens_reinit(struct device *dev, int *tsens_ret)
|
|
{
|
|
unsigned int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_TSENS,
|
|
.cmd = QCOM_SCM_TSENS_INIT_ID,
|
|
};
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
if (tsens_ret)
|
|
*tsens_ret = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_reboot(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_OEM_POWER,
|
|
.cmd = QCOM_SCM_OEM_POWER_REBOOT,
|
|
.owner = ARM_SMCCC_OWNER_OEM,
|
|
};
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(0);
|
|
|
|
return qcom_scm_call_atomic(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_ice_restore_cfg(struct device *dev)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_KEYSTORE,
|
|
.cmd = QCOM_SCM_ICE_RESTORE_KEY_ID,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(0);
|
|
|
|
return qcom_scm_call(dev, &desc);
|
|
}
|
|
|
|
int __qcom_scm_register_qsee_log_buf(struct device *dev, phys_addr_t buf,
|
|
size_t len)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_QSEELOG,
|
|
.cmd = QCOM_SCM_QSEELOG_REGISTER,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
|
|
desc.args[0] = buf;
|
|
desc.args[1] = len;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_query_encrypted_log_feature(struct device *dev, u64 *enabled)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_QSEELOG,
|
|
.cmd = QCOM_SCM_QUERY_ENCR_LOG_FEAT_ID,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.arginfo = QCOM_SCM_ARGS(0);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
if (enabled)
|
|
*enabled = desc.res[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_request_encrypted_log(struct device *dev, phys_addr_t buf,
|
|
size_t len, uint32_t log_id)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_QSEELOG,
|
|
.cmd = QCOM_SCM_REQUEST_ENCR_LOG_ID,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.args[0] = buf;
|
|
desc.args[1] = len;
|
|
desc.args[2] = log_id;
|
|
desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_invoke_smc_legacy(struct device *dev, phys_addr_t in_buf,
|
|
size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size,
|
|
int32_t *result, u64 *response_type, unsigned int *data)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMCINVOKE,
|
|
.cmd = QCOM_SCM_SMCINVOKE_INVOKE_LEGACY,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.args[0] = in_buf;
|
|
desc.args[1] = in_buf_size;
|
|
desc.args[2] = out_buf;
|
|
desc.args[3] = out_buf_size;
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call_noretry(dev, &desc);
|
|
|
|
if (result)
|
|
*result = desc.res[1];
|
|
|
|
if (response_type)
|
|
*response_type = desc.res[0];
|
|
|
|
if (data)
|
|
*data = desc.res[2];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_invoke_smc(struct device *dev, phys_addr_t in_buf,
|
|
size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size,
|
|
int32_t *result, u64 *response_type, unsigned int *data)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMCINVOKE,
|
|
.cmd = QCOM_SCM_SMCINVOKE_INVOKE,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.args[0] = in_buf;
|
|
desc.args[1] = in_buf_size;
|
|
desc.args[2] = out_buf;
|
|
desc.args[3] = out_buf_size;
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW,
|
|
QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call_noretry(dev, &desc);
|
|
|
|
if (result)
|
|
*result = desc.res[1];
|
|
|
|
if (response_type)
|
|
*response_type = desc.res[0];
|
|
|
|
if (data)
|
|
*data = desc.res[2];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_invoke_callback_response(struct device *dev, phys_addr_t out_buf,
|
|
size_t out_buf_size, int32_t *result, u64 *response_type,
|
|
unsigned int *data)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMCINVOKE,
|
|
.cmd = QCOM_SCM_SMCINVOKE_CB_RSP,
|
|
.owner = ARM_SMCCC_OWNER_TRUSTED_OS
|
|
};
|
|
|
|
desc.args[0] = out_buf;
|
|
desc.args[1] = out_buf_size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
|
|
|
|
ret = qcom_scm_call_noretry(dev, &desc);
|
|
|
|
if (result)
|
|
*result = desc.res[1];
|
|
|
|
if (response_type)
|
|
*response_type = desc.res[0];
|
|
|
|
if (data)
|
|
*data = desc.res[2];
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define TZ_SVC_MEMORY_PROTECTION 12 /* Memory protection service. */
|
|
|
|
#define TZ_MPU_LOCK_AUDIO_BUFFER \
|
|
TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_MEMORY_PROTECTION, 0x06)
|
|
|
|
#define TZ_MPU_LOCK_AUDIO_BUFFER_PARAM_ID \
|
|
TZ_SYSCALL_CREATE_PARAM_ID_2( \
|
|
TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_VAL)
|
|
int __qcom_scm_mem_protect_audio(struct device *dev, phys_addr_t paddr,
|
|
size_t size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = TZ_SVC_MEMORY_PROTECTION,
|
|
.cmd = 0x6,
|
|
.owner = TZ_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = paddr;
|
|
desc.args[1] = size;
|
|
desc.arginfo = QCOM_SCM_ARGS(2);
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __qcom_scm_qseecom_do(struct device *dev, u32 cmd_id, struct scm_desc *desc,
|
|
bool retry)
|
|
{
|
|
int _ret;
|
|
struct qcom_scm_desc _desc;
|
|
|
|
memcpy(&_desc.args, desc->args, sizeof(_desc.args));
|
|
_desc.owner = (cmd_id & 0x3f000000) >> 24;
|
|
_desc.svc = (cmd_id & 0xff00) >> 8;
|
|
_desc.cmd = (cmd_id & 0xff);
|
|
_desc.arginfo = desc->arginfo;
|
|
|
|
if (retry)
|
|
_ret = qcom_scm_call(dev, &_desc);
|
|
else
|
|
_ret = qcom_scm_call_noretry(dev, &_desc);
|
|
|
|
memcpy(desc->ret, &_desc.res, sizeof(_desc.res));
|
|
|
|
return _ret;
|
|
}
|
|
|
|
int __qcom_scm_paravirt_smmu_attach(struct device *dev, u64 sid,
|
|
u64 asid, u64 ste_pa, u64 ste_size,
|
|
u64 cd_pa, u64 cd_size)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
|
|
.cmd = ARM_SMMU_PARAVIRT_CMD,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
int ret;
|
|
|
|
desc.args[0] = SMMU_PARAVIRT_OP_ATTACH;
|
|
desc.args[1] = sid;
|
|
desc.args[2] = asid;
|
|
desc.args[3] = 0;
|
|
desc.args[4] = ste_pa;
|
|
desc.args[5] = ste_size;
|
|
desc.args[6] = cd_pa;
|
|
desc.args[7] = cd_size;
|
|
desc.arginfo = ARM_SMMU_PARAVIRT_DESCARG;
|
|
ret = qcom_scm_call(dev, &desc);
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_paravirt_tlb_inv(struct device *dev, u64 asid)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
|
|
.cmd = ARM_SMMU_PARAVIRT_CMD,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
int ret;
|
|
|
|
desc.args[0] = SMMU_PARAVIRT_OP_INVAL_ASID;
|
|
desc.args[1] = 0;
|
|
desc.args[2] = asid;
|
|
desc.args[3] = 0;
|
|
desc.args[4] = 0;
|
|
desc.args[5] = 0;
|
|
desc.args[6] = 0;
|
|
desc.args[7] = 0;
|
|
desc.arginfo = ARM_SMMU_PARAVIRT_DESCARG;
|
|
ret = qcom_scm_call_atomic(dev, &desc);
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
int __qcom_scm_paravirt_smmu_detach(struct device *dev, u64 sid)
|
|
{
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
|
|
.cmd = ARM_SMMU_PARAVIRT_CMD,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
int ret;
|
|
|
|
desc.args[0] = SMMU_PARAVIRT_OP_DETACH;
|
|
desc.args[1] = sid;
|
|
desc.args[2] = 0;
|
|
desc.args[3] = 0;
|
|
desc.args[4] = 0;
|
|
desc.args[5] = 0;
|
|
desc.args[6] = 0;
|
|
desc.args[7] = 0;
|
|
desc.arginfo = ARM_SMMU_PARAVIRT_DESCARG;
|
|
ret = qcom_scm_call(dev, &desc);
|
|
return ret ? : desc.res[0];
|
|
}
|
|
|
|
#ifdef CONFIG_QCOM_RTIC
|
|
|
|
#define TZ_RTIC_ENABLE_MEM_PROTECTION 0x4
|
|
int __init scm_mem_protection_init_do(struct device *dev)
|
|
{
|
|
int ret = 0, resp;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = SCM_SVC_RTIC,
|
|
.cmd = TZ_RTIC_ENABLE_MEM_PROTECTION,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = 0;
|
|
desc.arginfo = 0;
|
|
|
|
ret = qcom_scm_call(dev, &desc);
|
|
resp = desc.res[0];
|
|
|
|
if (ret == -1) {
|
|
pr_err("%s: SCM call not supported\n", __func__);
|
|
return ret;
|
|
} else if (ret || resp) {
|
|
pr_err("%s: SCM call failed\n", __func__);
|
|
if (ret)
|
|
return ret;
|
|
else
|
|
return resp;
|
|
}
|
|
return resp;
|
|
}
|
|
#endif
|
|
|
|
int __qcom_scm_ddrbw_profiler(struct device *dev, phys_addr_t in_buf,
|
|
size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size)
|
|
{
|
|
int ret;
|
|
struct qcom_scm_desc desc = {
|
|
.svc = QCOM_SCM_SVC_INFO,
|
|
.cmd = TZ_SVC_BW_PROF_ID,
|
|
.owner = ARM_SMCCC_OWNER_SIP,
|
|
};
|
|
|
|
desc.args[0] = in_buf;
|
|
desc.args[1] = in_buf_size;
|
|
desc.args[2] = out_buf;
|
|
desc.args[3] = out_buf_size;
|
|
desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW,
|
|
QCOM_SCM_VAL);
|
|
ret = qcom_scm_call(dev, &desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __qcom_scm_init(void)
|
|
{
|
|
|
|
#if IS_ENABLED(CONFIG_QCOM_SCM_QCPE)
|
|
/**
|
|
* The HAB connection should be opened before first SMC call.
|
|
* If not, there could be errors that might cause the
|
|
* system to crash.
|
|
*/
|
|
scm_qcpe_hab_open();
|
|
#endif
|
|
__query_convention();
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_QCOM_SCM_QCPE)
|
|
void __qcom_scm_qcpe_exit(void)
|
|
{
|
|
scm_qcpe_hab_close();
|
|
}
|
|
#endif
|