]> git.karo-electronics.de Git - linux-beck.git/blob - drivers/firmware/qcom_scm.c
firmware: qcom: scm: Peripheral Authentication Service
[linux-beck.git] / drivers / firmware / qcom_scm.c
1 /* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
2  * Copyright (C) 2015 Linaro Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 and
6  * only version 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14 #include <linux/platform_device.h>
15 #include <linux/module.h>
16 #include <linux/cpumask.h>
17 #include <linux/export.h>
18 #include <linux/dma-mapping.h>
19 #include <linux/types.h>
20 #include <linux/qcom_scm.h>
21 #include <linux/of.h>
22 #include <linux/of_platform.h>
23 #include <linux/clk.h>
24
25 #include "qcom_scm.h"
26
27 struct qcom_scm {
28         struct device *dev;
29         struct clk *core_clk;
30         struct clk *iface_clk;
31         struct clk *bus_clk;
32 };
33
34 static struct qcom_scm *__scm;
35
36 static int qcom_scm_clk_enable(void)
37 {
38         int ret;
39
40         ret = clk_prepare_enable(__scm->core_clk);
41         if (ret)
42                 goto bail;
43
44         ret = clk_prepare_enable(__scm->iface_clk);
45         if (ret)
46                 goto disable_core;
47
48         ret = clk_prepare_enable(__scm->bus_clk);
49         if (ret)
50                 goto disable_iface;
51
52         return 0;
53
54 disable_iface:
55         clk_disable_unprepare(__scm->iface_clk);
56 disable_core:
57         clk_disable_unprepare(__scm->core_clk);
58 bail:
59         return ret;
60 }
61
62 static void qcom_scm_clk_disable(void)
63 {
64         clk_disable_unprepare(__scm->core_clk);
65         clk_disable_unprepare(__scm->iface_clk);
66         clk_disable_unprepare(__scm->bus_clk);
67 }
68
69 /**
70  * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
71  * @entry: Entry point function for the cpus
72  * @cpus: The cpumask of cpus that will use the entry point
73  *
74  * Set the cold boot address of the cpus. Any cpu outside the supported
75  * range would be removed from the cpu present mask.
76  */
77 int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
78 {
79         return __qcom_scm_set_cold_boot_addr(entry, cpus);
80 }
81 EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
82
83 /**
84  * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
85  * @entry: Entry point function for the cpus
86  * @cpus: The cpumask of cpus that will use the entry point
87  *
88  * Set the Linux entry point for the SCM to transfer control to when coming
89  * out of a power down. CPU power down may be executed on cpuidle or hotplug.
90  */
91 int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
92 {
93         return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
94 }
95 EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
96
97 /**
98  * qcom_scm_cpu_power_down() - Power down the cpu
99  * @flags - Flags to flush cache
100  *
101  * This is an end point to power down cpu. If there was a pending interrupt,
102  * the control would return from this function, otherwise, the cpu jumps to the
103  * warm boot entry point set for this cpu upon reset.
104  */
105 void qcom_scm_cpu_power_down(u32 flags)
106 {
107         __qcom_scm_cpu_power_down(flags);
108 }
109 EXPORT_SYMBOL(qcom_scm_cpu_power_down);
110
111 /**
112  * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
113  *
114  * Return true if HDCP is supported, false if not.
115  */
116 bool qcom_scm_hdcp_available(void)
117 {
118         int ret = qcom_scm_clk_enable();
119
120         if (ret)
121                 return ret;
122
123         ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
124                                                 QCOM_SCM_CMD_HDCP);
125
126         qcom_scm_clk_disable();
127
128         return ret > 0 ? true : false;
129 }
130 EXPORT_SYMBOL(qcom_scm_hdcp_available);
131
132 /**
133  * qcom_scm_hdcp_req() - Send HDCP request.
134  * @req: HDCP request array
135  * @req_cnt: HDCP request array count
136  * @resp: response buffer passed to SCM
137  *
138  * Write HDCP register(s) through SCM.
139  */
140 int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
141 {
142         int ret = qcom_scm_clk_enable();
143
144         if (ret)
145                 return ret;
146
147         ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
148         qcom_scm_clk_disable();
149         return ret;
150 }
151 EXPORT_SYMBOL(qcom_scm_hdcp_req);
152
153 /**
154  * qcom_scm_pas_supported() - Check if the peripheral authentication service is
155  *                            available for the given peripherial
156  * @peripheral: peripheral id
157  *
158  * Returns true if PAS is supported for this peripheral, otherwise false.
159  */
160 bool qcom_scm_pas_supported(u32 peripheral)
161 {
162         int ret;
163
164         ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
165                                            QCOM_SCM_PAS_IS_SUPPORTED_CMD);
166         if (ret <= 0)
167                 return false;
168
169         return __qcom_scm_pas_supported(__scm->dev, peripheral);
170 }
171 EXPORT_SYMBOL(qcom_scm_pas_supported);
172
173 /**
174  * qcom_scm_pas_init_image() - Initialize peripheral authentication service
175  *                             state machine for a given peripheral, using the
176  *                             metadata
177  * @peripheral: peripheral id
178  * @metadata:   pointer to memory containing ELF header, program header table
179  *              and optional blob of data used for authenticating the metadata
180  *              and the rest of the firmware
181  * @size:       size of the metadata
182  *
183  * Returns 0 on success.
184  */
185 int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
186 {
187         dma_addr_t mdata_phys;
188         void *mdata_buf;
189         int ret;
190
191         /*
192          * During the scm call memory protection will be enabled for the meta
193          * data blob, so make sure it's physically contiguous, 4K aligned and
194          * non-cachable to avoid XPU violations.
195          */
196         mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
197                                        GFP_KERNEL);
198         if (!mdata_buf) {
199                 dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
200                 return -ENOMEM;
201         }
202         memcpy(mdata_buf, metadata, size);
203
204         ret = qcom_scm_clk_enable();
205         if (ret)
206                 goto free_metadata;
207
208         ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
209
210         qcom_scm_clk_disable();
211
212 free_metadata:
213         dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
214
215         return ret;
216 }
217 EXPORT_SYMBOL(qcom_scm_pas_init_image);
218
219 /**
220  * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
221  *                            for firmware loading
222  * @peripheral: peripheral id
223  * @addr:       start address of memory area to prepare
224  * @size:       size of the memory area to prepare
225  *
226  * Returns 0 on success.
227  */
228 int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
229 {
230         int ret;
231
232         ret = qcom_scm_clk_enable();
233         if (ret)
234                 return ret;
235
236         ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
237         qcom_scm_clk_disable();
238
239         return ret;
240 }
241 EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
242
243 /**
244  * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
245  *                                 and reset the remote processor
246  * @peripheral: peripheral id
247  *
248  * Return 0 on success.
249  */
250 int qcom_scm_pas_auth_and_reset(u32 peripheral)
251 {
252         int ret;
253
254         ret = qcom_scm_clk_enable();
255         if (ret)
256                 return ret;
257
258         ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
259         qcom_scm_clk_disable();
260
261         return ret;
262 }
263 EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
264
265 /**
266  * qcom_scm_pas_shutdown() - Shut down the remote processor
267  * @peripheral: peripheral id
268  *
269  * Returns 0 on success.
270  */
271 int qcom_scm_pas_shutdown(u32 peripheral)
272 {
273         int ret;
274
275         ret = qcom_scm_clk_enable();
276         if (ret)
277                 return ret;
278
279         ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
280         qcom_scm_clk_disable();
281
282         return ret;
283 }
284 EXPORT_SYMBOL(qcom_scm_pas_shutdown);
285
286 static int qcom_scm_probe(struct platform_device *pdev)
287 {
288         struct qcom_scm *scm;
289         int ret;
290
291         scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
292         if (!scm)
293                 return -ENOMEM;
294
295         scm->core_clk = devm_clk_get(&pdev->dev, "core");
296         if (IS_ERR(scm->core_clk)) {
297                 if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
298                         return PTR_ERR(scm->core_clk);
299
300                 scm->core_clk = NULL;
301         }
302
303         if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) {
304                 scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
305                 if (IS_ERR(scm->iface_clk)) {
306                         if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
307                                 dev_err(&pdev->dev, "failed to acquire iface clk\n");
308                         return PTR_ERR(scm->iface_clk);
309                 }
310
311                 scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
312                 if (IS_ERR(scm->bus_clk)) {
313                         if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
314                                 dev_err(&pdev->dev, "failed to acquire bus clk\n");
315                         return PTR_ERR(scm->bus_clk);
316                 }
317         }
318
319         /* vote for max clk rate for highest performance */
320         ret = clk_set_rate(scm->core_clk, INT_MAX);
321         if (ret)
322                 return ret;
323
324         __scm = scm;
325         __scm->dev = &pdev->dev;
326
327         __qcom_scm_init();
328
329         return 0;
330 }
331
332 static const struct of_device_id qcom_scm_dt_match[] = {
333         { .compatible = "qcom,scm-apq8064",},
334         { .compatible = "qcom,scm-msm8660",},
335         { .compatible = "qcom,scm-msm8960",},
336         { .compatible = "qcom,scm",},
337         {}
338 };
339
340 MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
341
342 static struct platform_driver qcom_scm_driver = {
343         .driver = {
344                 .name   = "qcom_scm",
345                 .of_match_table = qcom_scm_dt_match,
346         },
347         .probe = qcom_scm_probe,
348 };
349
350 static int __init qcom_scm_init(void)
351 {
352         struct device_node *np, *fw_np;
353         int ret;
354
355         fw_np = of_find_node_by_name(NULL, "firmware");
356
357         if (!fw_np)
358                 return -ENODEV;
359
360         np = of_find_matching_node(fw_np, qcom_scm_dt_match);
361
362         if (!np) {
363                 of_node_put(fw_np);
364                 return -ENODEV;
365         }
366
367         of_node_put(np);
368
369         ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
370
371         of_node_put(fw_np);
372
373         if (ret)
374                 return ret;
375
376         return platform_driver_register(&qcom_scm_driver);
377 }
378
379 arch_initcall(qcom_scm_init);
380
381 static void __exit qcom_scm_exit(void)
382 {
383         platform_driver_unregister(&qcom_scm_driver);
384 }
385 module_exit(qcom_scm_exit);
386
387 MODULE_DESCRIPTION("Qualcomm SCM driver");
388 MODULE_LICENSE("GPL v2");