0001
0002
0003
0004
0005
0006 #include <linux/slab.h>
0007 #include <linux/io.h>
0008 #include <linux/module.h>
0009 #include <linux/mutex.h>
0010 #include <linux/errno.h>
0011 #include <linux/err.h>
0012 #include <linux/qcom_scm.h>
0013 #include <linux/arm-smccc.h>
0014 #include <linux/dma-mapping.h>
0015
0016 #include "qcom_scm.h"
0017
0018 static DEFINE_MUTEX(qcom_scm_lock);
0019
0020
0021
0022
0023
0024
0025 struct arm_smccc_args {
0026 unsigned long args[8];
0027 };
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054 struct scm_legacy_command {
0055 __le32 len;
0056 __le32 buf_offset;
0057 __le32 resp_hdr_offset;
0058 __le32 id;
0059 __le32 buf[];
0060 };
0061
0062
0063
0064
0065
0066
0067
0068 struct scm_legacy_response {
0069 __le32 len;
0070 __le32 buf_offset;
0071 __le32 is_complete;
0072 };
0073
0074
0075
0076
0077
0078
0079
0080 static inline struct scm_legacy_response *scm_legacy_command_to_response(
0081 const struct scm_legacy_command *cmd)
0082 {
0083 return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
0084 }
0085
0086
0087
0088
0089
0090
0091
0092 static inline void *scm_legacy_get_command_buffer(
0093 const struct scm_legacy_command *cmd)
0094 {
0095 return (void *)cmd->buf;
0096 }
0097
0098
0099
0100
0101
0102
0103
0104 static inline void *scm_legacy_get_response_buffer(
0105 const struct scm_legacy_response *rsp)
0106 {
0107 return (void *)rsp + le32_to_cpu(rsp->buf_offset);
0108 }
0109
0110 static void __scm_legacy_do(const struct arm_smccc_args *smc,
0111 struct arm_smccc_res *res)
0112 {
0113 do {
0114 arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2],
0115 smc->args[3], smc->args[4], smc->args[5],
0116 smc->args[6], smc->args[7], res);
0117 } while (res->a0 == QCOM_SCM_INTERRUPTED);
0118 }
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
0135 struct qcom_scm_res *res)
0136 {
0137 u8 arglen = desc->arginfo & 0xf;
0138 int ret = 0, context_id;
0139 unsigned int i;
0140 struct scm_legacy_command *cmd;
0141 struct scm_legacy_response *rsp;
0142 struct arm_smccc_args smc = {0};
0143 struct arm_smccc_res smc_res;
0144 const size_t cmd_len = arglen * sizeof(__le32);
0145 const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32);
0146 size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
0147 dma_addr_t cmd_phys;
0148 __le32 *arg_buf;
0149 const __le32 *res_buf;
0150
0151 cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
0152 if (!cmd)
0153 return -ENOMEM;
0154
0155 cmd->len = cpu_to_le32(alloc_len);
0156 cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
0157 cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
0158 cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd));
0159
0160 arg_buf = scm_legacy_get_command_buffer(cmd);
0161 for (i = 0; i < arglen; i++)
0162 arg_buf[i] = cpu_to_le32(desc->args[i]);
0163
0164 rsp = scm_legacy_command_to_response(cmd);
0165
0166 cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
0167 if (dma_mapping_error(dev, cmd_phys)) {
0168 kfree(cmd);
0169 return -ENOMEM;
0170 }
0171
0172 smc.args[0] = 1;
0173 smc.args[1] = (unsigned long)&context_id;
0174 smc.args[2] = cmd_phys;
0175
0176 mutex_lock(&qcom_scm_lock);
0177 __scm_legacy_do(&smc, &smc_res);
0178 if (smc_res.a0)
0179 ret = qcom_scm_remap_error(smc_res.a0);
0180 mutex_unlock(&qcom_scm_lock);
0181 if (ret)
0182 goto out;
0183
0184 do {
0185 dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
0186 sizeof(*rsp), DMA_FROM_DEVICE);
0187 } while (!rsp->is_complete);
0188
0189 dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
0190 le32_to_cpu(rsp->buf_offset),
0191 resp_len, DMA_FROM_DEVICE);
0192
0193 if (res) {
0194 res_buf = scm_legacy_get_response_buffer(rsp);
0195 for (i = 0; i < MAX_QCOM_SCM_RETS; i++)
0196 res->result[i] = le32_to_cpu(res_buf[i]);
0197 }
0198 out:
0199 dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
0200 kfree(cmd);
0201 return ret;
0202 }
0203
0204 #define SCM_LEGACY_ATOMIC_N_REG_ARGS 5
0205 #define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2
0206 #define SCM_LEGACY_CLASS_REGISTER (0x2 << 8)
0207 #define SCM_LEGACY_MASK_IRQS BIT(5)
0208 #define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \
0209 ((SCM_LEGACY_FNID(svc, cmd) << 12) | \
0210 SCM_LEGACY_CLASS_REGISTER | \
0211 SCM_LEGACY_MASK_IRQS | \
0212 (n & 0xf))
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224 int scm_legacy_call_atomic(struct device *unused,
0225 const struct qcom_scm_desc *desc,
0226 struct qcom_scm_res *res)
0227 {
0228 int context_id;
0229 struct arm_smccc_res smc_res;
0230 size_t arglen = desc->arginfo & 0xf;
0231
0232 BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS);
0233
0234 arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen),
0235 (unsigned long)&context_id,
0236 desc->args[0], desc->args[1], desc->args[2],
0237 desc->args[3], desc->args[4], 0, &smc_res);
0238
0239 if (res) {
0240 res->result[0] = smc_res.a1;
0241 res->result[1] = smc_res.a2;
0242 res->result[2] = smc_res.a3;
0243 }
0244
0245 return smc_res.a0;
0246 }