mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
Merge "firmware: qcom_scm: Merge legacy and SMCCC conventions"
This commit is contained in:
commit
6b47d58c25
1 changed files with 315 additions and 8 deletions
|
|
@ -71,6 +71,83 @@ static DEFINE_MUTEX(qcom_scm_lock);
|
|||
#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 },
|
||||
};
|
||||
|
||||
static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
|
||||
struct arm_smccc_res *res)
|
||||
{
|
||||
|
|
@ -90,7 +167,7 @@ static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
|
|||
} while (res->a0 == QCOM_SCM_INTERRUPTED);
|
||||
}
|
||||
|
||||
static int ___qcom_scm_call_smccc(struct device *dev,
|
||||
static int qcom_scm_call_smccc(struct device *dev,
|
||||
struct qcom_scm_desc *desc, bool atomic)
|
||||
{
|
||||
int arglen = desc->arginfo & 0xf;
|
||||
|
|
@ -176,6 +253,169 @@ static int ___qcom_scm_call_smccc(struct device *dev,
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -189,7 +429,7 @@ static int ___qcom_scm_call_smccc(struct device *dev,
|
|||
static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc)
|
||||
{
|
||||
might_sleep();
|
||||
return ___qcom_scm_call_smccc(dev, desc, false);
|
||||
return qcom_scm_call_smccc(dev, desc, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -205,7 +445,7 @@ static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc)
|
|||
*/
|
||||
static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc)
|
||||
{
|
||||
return ___qcom_scm_call_smccc(dev, desc, true);
|
||||
return qcom_scm_call_smccc(dev, desc, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -219,7 +459,35 @@ static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc)
|
|||
int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry,
|
||||
const cpumask_t *cpus)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -234,7 +502,37 @@ int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry,
|
|||
int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
|
||||
const cpumask_t *cpus)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
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] = virt_to_phys(entry);
|
||||
desc.args[1] = flags;
|
||||
ret = qcom_scm_call(dev, &desc);
|
||||
if (!ret) {
|
||||
for_each_cpu(cpu, cpus)
|
||||
qcom_scm_wb[cpu].entry = entry;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,6 +545,15 @@ int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
|
|||
*/
|
||||
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)
|
||||
|
|
@ -279,7 +586,7 @@ int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
|
|||
desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2);
|
||||
|
||||
return qcom_scm_call(dev, &desc);
|
||||
return qcom_scm_call_atomic(dev, &desc);
|
||||
}
|
||||
|
||||
bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
|
||||
|
|
@ -403,7 +710,7 @@ int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
|
|||
desc.args[0] = addr;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
|
||||
ret = qcom_scm_call(dev, &desc);
|
||||
ret = qcom_scm_call_atomic(dev, &desc);
|
||||
if (ret >= 0)
|
||||
*val = desc.res[0];
|
||||
|
||||
|
|
@ -422,7 +729,7 @@ int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
|
|||
desc.args[1] = val;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2);
|
||||
|
||||
return qcom_scm_call(dev, &desc);
|
||||
return qcom_scm_call_atomic(dev, &desc);
|
||||
}
|
||||
|
||||
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
|
||||
|
|
|
|||
Loading…
Reference in a new issue