firmware: qcom_scm: Remove thin wrappers
[sfrench/cifs-2.6.git] / drivers / firmware / qcom_scm-64.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
3  */
4
5 #include <linux/io.h>
6 #include <linux/errno.h>
7 #include <linux/delay.h>
8 #include <linux/mutex.h>
9 #include <linux/slab.h>
10 #include <linux/types.h>
11 #include <linux/qcom_scm.h>
12 #include <linux/arm-smccc.h>
13 #include <linux/dma-mapping.h>
14
15 #include "qcom_scm.h"
16
17 #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
18
19 /**
20  * struct arm_smccc_args
21  * @args:       The array of values used in registers in smc instruction
22  */
23 struct arm_smccc_args {
24         unsigned long args[8];
25 };
26
27 static u64 qcom_smccc_convention = -1;
28 static DEFINE_MUTEX(qcom_scm_lock);
29
30 #define QCOM_SCM_EBUSY_WAIT_MS 30
31 #define QCOM_SCM_EBUSY_MAX_RETRY 20
32
33 #define SCM_SMC_N_REG_ARGS      4
34 #define SCM_SMC_FIRST_EXT_IDX   (SCM_SMC_N_REG_ARGS - 1)
35 #define SCM_SMC_N_EXT_ARGS      (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1)
36 #define SCM_SMC_FIRST_REG_IDX   2
37 #define SCM_SMC_LAST_REG_IDX    (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1)
38
39 static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
40                                struct arm_smccc_res *res)
41 {
42         unsigned long a0 = smc->args[0];
43         struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
44
45         quirk.state.a6 = 0;
46
47         do {
48                 arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
49                                     smc->args[3], smc->args[4], smc->args[5],
50                                     quirk.state.a6, smc->args[7], res, &quirk);
51
52                 if (res->a0 == QCOM_SCM_INTERRUPTED)
53                         a0 = res->a0;
54
55         } while (res->a0 == QCOM_SCM_INTERRUPTED);
56 }
57
58 static void __scm_smc_do(const struct arm_smccc_args *smc,
59                          struct arm_smccc_res *res, bool atomic)
60 {
61         int retry_count = 0;
62
63         if (atomic) {
64                 __scm_smc_do_quirk(smc, res);
65                 return;
66         }
67
68         do {
69                 mutex_lock(&qcom_scm_lock);
70
71                 __scm_smc_do_quirk(smc, res);
72
73                 mutex_unlock(&qcom_scm_lock);
74
75                 if (res->a0 == QCOM_SCM_V2_EBUSY) {
76                         if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
77                                 break;
78                         msleep(QCOM_SCM_EBUSY_WAIT_MS);
79                 }
80         }  while (res->a0 == QCOM_SCM_V2_EBUSY);
81 }
82
83 static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
84                           struct qcom_scm_res *res, bool atomic)
85 {
86         int arglen = desc->arginfo & 0xf;
87         int i;
88         dma_addr_t args_phys = 0;
89         void *args_virt = NULL;
90         size_t alloc_len;
91         gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
92         u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
93         struct arm_smccc_res smc_res;
94         struct arm_smccc_args smc = {0};
95
96         smc.args[0] = ARM_SMCCC_CALL_VAL(
97                 smccc_call_type,
98                 qcom_smccc_convention,
99                 desc->owner,
100                 SCM_SMC_FNID(desc->svc, desc->cmd));
101         smc.args[1] = desc->arginfo;
102         for (i = 0; i < SCM_SMC_N_REG_ARGS; i++)
103                 smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i];
104
105         if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) {
106                 alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64);
107                 args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag);
108
109                 if (!args_virt)
110                         return -ENOMEM;
111
112                 if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
113                         __le32 *args = args_virt;
114
115                         for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
116                                 args[i] = cpu_to_le32(desc->args[i +
117                                                       SCM_SMC_FIRST_EXT_IDX]);
118                 } else {
119                         __le64 *args = args_virt;
120
121                         for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
122                                 args[i] = cpu_to_le64(desc->args[i +
123                                                       SCM_SMC_FIRST_EXT_IDX]);
124                 }
125
126                 args_phys = dma_map_single(dev, args_virt, alloc_len,
127                                            DMA_TO_DEVICE);
128
129                 if (dma_mapping_error(dev, args_phys)) {
130                         kfree(args_virt);
131                         return -ENOMEM;
132                 }
133
134                 smc.args[SCM_SMC_LAST_REG_IDX] = args_phys;
135         }
136
137         __scm_smc_do(&smc, &smc_res, atomic);
138
139         if (args_virt) {
140                 dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
141                 kfree(args_virt);
142         }
143
144         if (res) {
145                 res->result[0] = smc_res.a1;
146                 res->result[1] = smc_res.a2;
147                 res->result[2] = smc_res.a3;
148         }
149
150         return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
151 }
152
153 /**
154  * qcom_scm_call() - Invoke a syscall in the secure world
155  * @dev:        device
156  * @svc_id:     service identifier
157  * @cmd_id:     command identifier
158  * @desc:       Descriptor structure containing arguments and return values
159  *
160  * Sends a command to the SCM and waits for the command to finish processing.
161  * This should *only* be called in pre-emptible context.
162  */
163 int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
164                   struct qcom_scm_res *res)
165 {
166         might_sleep();
167         return __scm_smc_call(dev, desc, res, false);
168 }
169
170 /**
171  * qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
172  * @dev:        device
173  * @svc_id:     service identifier
174  * @cmd_id:     command identifier
175  * @desc:       Descriptor structure containing arguments and return values
176  * @res:        Structure containing results from SMC/HVC call
177  *
178  * Sends a command to the SCM and waits for the command to finish processing.
179  * This can be called in atomic context.
180  */
181 int qcom_scm_call_atomic(struct device *dev, const struct qcom_scm_desc *desc,
182                          struct qcom_scm_res *res)
183 {
184         return __scm_smc_call(dev, desc, res, true);
185 }
186
187 int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
188 {
189         int ret;
190         struct qcom_scm_desc desc = {
191                 .svc = QCOM_SCM_SVC_INFO,
192                 .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
193                 .owner = ARM_SMCCC_OWNER_SIP,
194         };
195         struct qcom_scm_res res;
196
197         desc.arginfo = QCOM_SCM_ARGS(1);
198         desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
199                         (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
200
201         ret = qcom_scm_call(dev, &desc, &res);
202
203         return ret ? : res.result[0];
204 }
205
206 void __qcom_scm_init(void)
207 {
208         struct qcom_scm_desc desc = {
209                 .svc = QCOM_SCM_SVC_INFO,
210                 .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
211                 .args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
212                                         QCOM_SCM_INFO_IS_CALL_AVAIL) |
213                            (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
214                 .arginfo = QCOM_SCM_ARGS(1),
215                 .owner = ARM_SMCCC_OWNER_SIP,
216         };
217         struct qcom_scm_res res;
218         int ret;
219
220         qcom_smccc_convention = ARM_SMCCC_SMC_64;
221         // Device isn't required as there is only one argument - no device
222         // needed to dma_map_single to secure world
223         ret = qcom_scm_call_atomic(NULL, &desc, &res);
224         if (!ret && res.result[0] == 1)
225                 goto out;
226
227         qcom_smccc_convention = ARM_SMCCC_SMC_32;
228         ret = qcom_scm_call_atomic(NULL, &desc, &res);
229         if (!ret && res.result[0] == 1)
230                 goto out;
231
232         qcom_smccc_convention = -1;
233         BUG();
234 out:
235         pr_info("QCOM SCM SMC Convention: %lld\n", qcom_smccc_convention);
236 }