1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/platform_device.h>
18#include <linux/init.h>
19#include <linux/cpumask.h>
20#include <linux/export.h>
21#include <linux/dma-mapping.h>
22#include <linux/types.h>
23#include <linux/qcom_scm.h>
24#include <linux/of.h>
25#include <linux/of_platform.h>
26#include <linux/clk.h>
27#include <linux/reset-controller.h>
28
29#include "qcom_scm.h"
30
31#define SCM_HAS_CORE_CLK BIT(0)
32#define SCM_HAS_IFACE_CLK BIT(1)
33#define SCM_HAS_BUS_CLK BIT(2)
34
35struct qcom_scm {
36 struct device *dev;
37 struct clk *core_clk;
38 struct clk *iface_clk;
39 struct clk *bus_clk;
40 struct reset_controller_dev reset;
41};
42
43static struct qcom_scm *__scm;
44
45static int qcom_scm_clk_enable(void)
46{
47 int ret;
48
49 ret = clk_prepare_enable(__scm->core_clk);
50 if (ret)
51 goto bail;
52
53 ret = clk_prepare_enable(__scm->iface_clk);
54 if (ret)
55 goto disable_core;
56
57 ret = clk_prepare_enable(__scm->bus_clk);
58 if (ret)
59 goto disable_iface;
60
61 return 0;
62
63disable_iface:
64 clk_disable_unprepare(__scm->iface_clk);
65disable_core:
66 clk_disable_unprepare(__scm->core_clk);
67bail:
68 return ret;
69}
70
71static void qcom_scm_clk_disable(void)
72{
73 clk_disable_unprepare(__scm->core_clk);
74 clk_disable_unprepare(__scm->iface_clk);
75 clk_disable_unprepare(__scm->bus_clk);
76}
77
78
79
80
81
82
83
84
85
86int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
87{
88 return __qcom_scm_set_cold_boot_addr(entry, cpus);
89}
90EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
91
92
93
94
95
96
97
98
99
100int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
101{
102 return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
103}
104EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
105
106
107
108
109
110
111
112
113
114void qcom_scm_cpu_power_down(u32 flags)
115{
116 __qcom_scm_cpu_power_down(flags);
117}
118EXPORT_SYMBOL(qcom_scm_cpu_power_down);
119
120
121
122
123
124
125bool qcom_scm_hdcp_available(void)
126{
127 int ret = qcom_scm_clk_enable();
128
129 if (ret)
130 return ret;
131
132 ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
133 QCOM_SCM_CMD_HDCP);
134
135 qcom_scm_clk_disable();
136
137 return ret > 0 ? true : false;
138}
139EXPORT_SYMBOL(qcom_scm_hdcp_available);
140
141
142
143
144
145
146
147
148
149int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
150{
151 int ret = qcom_scm_clk_enable();
152
153 if (ret)
154 return ret;
155
156 ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
157 qcom_scm_clk_disable();
158 return ret;
159}
160EXPORT_SYMBOL(qcom_scm_hdcp_req);
161
162
163
164
165
166
167
168
169bool qcom_scm_pas_supported(u32 peripheral)
170{
171 int ret;
172
173 ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
174 QCOM_SCM_PAS_IS_SUPPORTED_CMD);
175 if (ret <= 0)
176 return false;
177
178 return __qcom_scm_pas_supported(__scm->dev, peripheral);
179}
180EXPORT_SYMBOL(qcom_scm_pas_supported);
181
182
183
184
185
186
187
188
189
190
191
192
193
194int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
195{
196 dma_addr_t mdata_phys;
197 void *mdata_buf;
198 int ret;
199
200
201
202
203
204
205 mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
206 GFP_KERNEL);
207 if (!mdata_buf) {
208 dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
209 return -ENOMEM;
210 }
211 memcpy(mdata_buf, metadata, size);
212
213 ret = qcom_scm_clk_enable();
214 if (ret)
215 goto free_metadata;
216
217 ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
218
219 qcom_scm_clk_disable();
220
221free_metadata:
222 dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
223
224 return ret;
225}
226EXPORT_SYMBOL(qcom_scm_pas_init_image);
227
228
229
230
231
232
233
234
235
236
237int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
238{
239 int ret;
240
241 ret = qcom_scm_clk_enable();
242 if (ret)
243 return ret;
244
245 ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
246 qcom_scm_clk_disable();
247
248 return ret;
249}
250EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
251
252
253
254
255
256
257
258
259int qcom_scm_pas_auth_and_reset(u32 peripheral)
260{
261 int ret;
262
263 ret = qcom_scm_clk_enable();
264 if (ret)
265 return ret;
266
267 ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
268 qcom_scm_clk_disable();
269
270 return ret;
271}
272EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
273
274
275
276
277
278
279
280int qcom_scm_pas_shutdown(u32 peripheral)
281{
282 int ret;
283
284 ret = qcom_scm_clk_enable();
285 if (ret)
286 return ret;
287
288 ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
289 qcom_scm_clk_disable();
290
291 return ret;
292}
293EXPORT_SYMBOL(qcom_scm_pas_shutdown);
294
295static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
296 unsigned long idx)
297{
298 if (idx != 0)
299 return -EINVAL;
300
301 return __qcom_scm_pas_mss_reset(__scm->dev, 1);
302}
303
304static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
305 unsigned long idx)
306{
307 if (idx != 0)
308 return -EINVAL;
309
310 return __qcom_scm_pas_mss_reset(__scm->dev, 0);
311}
312
313static const struct reset_control_ops qcom_scm_pas_reset_ops = {
314 .assert = qcom_scm_pas_reset_assert,
315 .deassert = qcom_scm_pas_reset_deassert,
316};
317
318int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
319{
320 return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare);
321}
322EXPORT_SYMBOL(qcom_scm_restore_sec_cfg);
323
324int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size)
325{
326 return __qcom_scm_iommu_secure_ptbl_size(__scm->dev, spare, size);
327}
328EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size);
329
330int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
331{
332 return __qcom_scm_iommu_secure_ptbl_init(__scm->dev, addr, size, spare);
333}
334EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
335
336
337
338
339bool qcom_scm_is_available(void)
340{
341 return !!__scm;
342}
343EXPORT_SYMBOL(qcom_scm_is_available);
344
345int qcom_scm_set_remote_state(u32 state, u32 id)
346{
347 return __qcom_scm_set_remote_state(__scm->dev, state, id);
348}
349EXPORT_SYMBOL(qcom_scm_set_remote_state);
350
351static int qcom_scm_probe(struct platform_device *pdev)
352{
353 struct qcom_scm *scm;
354 unsigned long clks;
355 int ret;
356
357 scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
358 if (!scm)
359 return -ENOMEM;
360
361 clks = (unsigned long)of_device_get_match_data(&pdev->dev);
362 if (clks & SCM_HAS_CORE_CLK) {
363 scm->core_clk = devm_clk_get(&pdev->dev, "core");
364 if (IS_ERR(scm->core_clk)) {
365 if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
366 dev_err(&pdev->dev,
367 "failed to acquire core clk\n");
368 return PTR_ERR(scm->core_clk);
369 }
370 }
371
372 if (clks & SCM_HAS_IFACE_CLK) {
373 scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
374 if (IS_ERR(scm->iface_clk)) {
375 if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
376 dev_err(&pdev->dev,
377 "failed to acquire iface clk\n");
378 return PTR_ERR(scm->iface_clk);
379 }
380 }
381
382 if (clks & SCM_HAS_BUS_CLK) {
383 scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
384 if (IS_ERR(scm->bus_clk)) {
385 if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
386 dev_err(&pdev->dev,
387 "failed to acquire bus clk\n");
388 return PTR_ERR(scm->bus_clk);
389 }
390 }
391
392 scm->reset.ops = &qcom_scm_pas_reset_ops;
393 scm->reset.nr_resets = 1;
394 scm->reset.of_node = pdev->dev.of_node;
395 ret = devm_reset_controller_register(&pdev->dev, &scm->reset);
396 if (ret)
397 return ret;
398
399
400 ret = clk_set_rate(scm->core_clk, INT_MAX);
401 if (ret)
402 return ret;
403
404 __scm = scm;
405 __scm->dev = &pdev->dev;
406
407 __qcom_scm_init();
408
409 return 0;
410}
411
412static const struct of_device_id qcom_scm_dt_match[] = {
413 { .compatible = "qcom,scm-apq8064",
414
415 },
416 { .compatible = "qcom,scm-msm8660",
417 .data = (void *) SCM_HAS_CORE_CLK,
418 },
419 { .compatible = "qcom,scm-msm8960",
420 .data = (void *) SCM_HAS_CORE_CLK,
421 },
422 { .compatible = "qcom,scm-msm8996",
423 .data = NULL,
424 },
425 { .compatible = "qcom,scm",
426 .data = (void *)(SCM_HAS_CORE_CLK
427 | SCM_HAS_IFACE_CLK
428 | SCM_HAS_BUS_CLK),
429 },
430 {}
431};
432
433static struct platform_driver qcom_scm_driver = {
434 .driver = {
435 .name = "qcom_scm",
436 .of_match_table = qcom_scm_dt_match,
437 },
438 .probe = qcom_scm_probe,
439};
440
441static int __init qcom_scm_init(void)
442{
443 struct device_node *np, *fw_np;
444 int ret;
445
446 fw_np = of_find_node_by_name(NULL, "firmware");
447
448 if (!fw_np)
449 return -ENODEV;
450
451 np = of_find_matching_node(fw_np, qcom_scm_dt_match);
452
453 if (!np) {
454 of_node_put(fw_np);
455 return -ENODEV;
456 }
457
458 of_node_put(np);
459
460 ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
461
462 of_node_put(fw_np);
463
464 if (ret)
465 return ret;
466
467 return platform_driver_register(&qcom_scm_driver);
468}
469subsys_initcall(qcom_scm_init);
470