]>
Commit | Line | Data |
---|---|---|
9626b699 | 1 | /* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. |
2ce76a6a | 2 | * Copyright (C) 2015 Linaro Ltd. |
2a1eb58a SB |
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 | * | |
2a1eb58a | 13 | */ |
59def4d6 | 14 | #include <linux/platform_device.h> |
2fa19f5a | 15 | #include <linux/module.h> |
b6a1dfbc KG |
16 | #include <linux/cpumask.h> |
17 | #include <linux/export.h> | |
18 | #include <linux/types.h> | |
916f743d | 19 | #include <linux/qcom_scm.h> |
2fa19f5a AG |
20 | #include <linux/of.h> |
21 | #include <linux/clk.h> | |
2a1eb58a | 22 | |
b6a1dfbc | 23 | #include "qcom_scm.h" |
a353e4a0 | 24 | |
2fa19f5a AG |
25 | struct qcom_scm { |
26 | struct clk *core_clk; | |
27 | struct clk *iface_clk; | |
28 | struct clk *bus_clk; | |
29 | }; | |
30 | ||
31 | static struct qcom_scm *__scm; | |
32 | ||
33 | static int qcom_scm_clk_enable(void) | |
34 | { | |
35 | int ret; | |
36 | ||
49a06182 SK |
37 | if(__scm->core_clk) { |
38 | ret = clk_prepare_enable(__scm->core_clk); | |
39 | if (ret) | |
40 | goto bail; | |
41 | } | |
42 | ||
43 | if(__scm->iface_clk) { | |
44 | ret = clk_prepare_enable(__scm->iface_clk); | |
45 | if (ret) | |
46 | goto disable_core; | |
47 | } | |
48 | ||
49 | if(__scm->bus_clk) { | |
50 | ret = clk_prepare_enable(__scm->bus_clk); | |
51 | if (ret) | |
52 | goto disable_iface; | |
53 | } | |
2fa19f5a AG |
54 | |
55 | return 0; | |
56 | ||
57 | disable_iface: | |
49a06182 SK |
58 | if(__scm->iface_clk) |
59 | clk_disable_unprepare(__scm->iface_clk); | |
2fa19f5a | 60 | disable_core: |
49a06182 SK |
61 | if(__scm->core_clk) |
62 | clk_disable_unprepare(__scm->core_clk); | |
2fa19f5a AG |
63 | bail: |
64 | return ret; | |
65 | } | |
66 | ||
67 | static void qcom_scm_clk_disable(void) | |
68 | { | |
49a06182 SK |
69 | if(__scm->core_clk) |
70 | clk_disable_unprepare(__scm->core_clk); | |
71 | if(__scm->iface_clk) | |
72 | clk_disable_unprepare(__scm->iface_clk); | |
73 | if(__scm->bus_clk) | |
74 | clk_disable_unprepare(__scm->bus_clk); | |
2fa19f5a AG |
75 | } |
76 | ||
a353e4a0 LI |
77 | /** |
78 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | |
79 | * @entry: Entry point function for the cpus | |
80 | * @cpus: The cpumask of cpus that will use the entry point | |
81 | * | |
82 | * Set the cold boot address of the cpus. Any cpu outside the supported | |
83 | * range would be removed from the cpu present mask. | |
84 | */ | |
85 | int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | |
86 | { | |
b6a1dfbc | 87 | return __qcom_scm_set_cold_boot_addr(entry, cpus); |
a353e4a0 LI |
88 | } |
89 | EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); | |
2ce76a6a LI |
90 | |
91 | /** | |
92 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus | |
93 | * @entry: Entry point function for the cpus | |
94 | * @cpus: The cpumask of cpus that will use the entry point | |
95 | * | |
96 | * Set the Linux entry point for the SCM to transfer control to when coming | |
97 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | |
98 | */ | |
99 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | |
100 | { | |
b6a1dfbc | 101 | return __qcom_scm_set_warm_boot_addr(entry, cpus); |
2ce76a6a LI |
102 | } |
103 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); | |
767b0235 | 104 | |
767b0235 LI |
105 | /** |
106 | * qcom_scm_cpu_power_down() - Power down the cpu | |
107 | * @flags - Flags to flush cache | |
108 | * | |
109 | * This is an end point to power down cpu. If there was a pending interrupt, | |
110 | * the control would return from this function, otherwise, the cpu jumps to the | |
111 | * warm boot entry point set for this cpu upon reset. | |
112 | */ | |
113 | void qcom_scm_cpu_power_down(u32 flags) | |
114 | { | |
b6a1dfbc | 115 | __qcom_scm_cpu_power_down(flags); |
767b0235 LI |
116 | } |
117 | EXPORT_SYMBOL(qcom_scm_cpu_power_down); | |
9626b699 | 118 | |
119 | /** | |
120 | * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. | |
121 | * | |
122 | * Return true if HDCP is supported, false if not. | |
123 | */ | |
124 | bool qcom_scm_hdcp_available(void) | |
125 | { | |
2fa19f5a AG |
126 | int ret = qcom_scm_clk_enable(); |
127 | ||
128 | if (ret) | |
129 | goto clk_err; | |
9626b699 | 130 | |
131 | ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, | |
2fa19f5a | 132 | QCOM_SCM_CMD_HDCP); |
9626b699 | 133 | |
2fa19f5a AG |
134 | qcom_scm_clk_disable(); |
135 | ||
136 | clk_err: | |
9626b699 | 137 | return (ret > 0) ? true : false; |
138 | } | |
139 | EXPORT_SYMBOL(qcom_scm_hdcp_available); | |
140 | ||
141 | /** | |
142 | * qcom_scm_hdcp_req() - Send HDCP request. | |
143 | * @req: HDCP request array | |
144 | * @req_cnt: HDCP request array count | |
145 | * @resp: response buffer passed to SCM | |
146 | * | |
147 | * Write HDCP register(s) through SCM. | |
148 | */ | |
149 | int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | |
150 | { | |
2fa19f5a AG |
151 | int ret = qcom_scm_clk_enable(); |
152 | ||
153 | if (ret) | |
154 | return ret; | |
155 | ||
156 | ret = __qcom_scm_hdcp_req(req, req_cnt, resp); | |
157 | qcom_scm_clk_disable(); | |
158 | return ret; | |
9626b699 | 159 | } |
160 | EXPORT_SYMBOL(qcom_scm_hdcp_req); | |
ccd42027 | 161 | |
b13079ce SK |
162 | int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp) |
163 | { | |
164 | return __qcom_scm_restart_proc(pid, restart, resp); | |
165 | } | |
166 | EXPORT_SYMBOL(qcom_scm_restart_proc); | |
ccd42027 BA |
167 | /** |
168 | * qcom_scm_pas_supported() - Check if the peripheral authentication service is | |
169 | * available for the given peripherial | |
170 | * @peripheral: peripheral id | |
171 | * | |
172 | * Returns true if PAS is supported for this peripheral, otherwise false. | |
173 | */ | |
174 | bool qcom_scm_pas_supported(u32 peripheral) | |
175 | { | |
176 | int ret; | |
177 | ||
178 | ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_PIL, | |
179 | QCOM_SCM_PAS_IS_SUPPORTED_CMD); | |
180 | if (ret <= 0) | |
181 | return false; | |
182 | ||
183 | return __qcom_scm_pas_supported(peripheral); | |
184 | } | |
185 | EXPORT_SYMBOL(qcom_scm_pas_supported); | |
186 | ||
187 | /** | |
188 | * qcom_scm_pas_init_image() - Initialize peripheral authentication service | |
189 | * state machine for a given peripheral, using the | |
190 | * metadata | |
191 | * @peripheral: peripheral id | |
192 | * @metadata: pointer to memory containing ELF header, program header table | |
193 | * and optional blob of data used for authenticating the metadata | |
194 | * and the rest of the firmware | |
195 | * @size: size of the metadata | |
196 | * | |
197 | * Returns 0 on success. | |
198 | */ | |
59def4d6 | 199 | int qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size) |
ccd42027 | 200 | { |
59def4d6 | 201 | return __qcom_scm_pas_init_image(dev, peripheral, metadata, size); |
ccd42027 BA |
202 | } |
203 | EXPORT_SYMBOL(qcom_scm_pas_init_image); | |
204 | ||
205 | /** | |
206 | * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral | |
207 | * for firmware loading | |
208 | * @peripheral: peripheral id | |
209 | * @addr: start address of memory area to prepare | |
210 | * @size: size of the memory area to prepare | |
211 | * | |
212 | * Returns 0 on success. | |
213 | */ | |
214 | int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) | |
215 | { | |
216 | return __qcom_scm_pas_mem_setup(peripheral, addr, size); | |
217 | } | |
218 | EXPORT_SYMBOL(qcom_scm_pas_mem_setup); | |
219 | ||
220 | /** | |
221 | * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware | |
222 | * and reset the remote processor | |
223 | * @peripheral: peripheral id | |
224 | * | |
225 | * Return 0 on success. | |
226 | */ | |
227 | int qcom_scm_pas_auth_and_reset(u32 peripheral) | |
228 | { | |
229 | return __qcom_scm_pas_auth_and_reset(peripheral); | |
230 | } | |
231 | EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); | |
232 | ||
233 | /** | |
234 | * qcom_scm_pas_shutdown() - Shut down the remote processor | |
235 | * @peripheral: peripheral id | |
236 | * | |
237 | * Returns 0 on success. | |
238 | */ | |
239 | int qcom_scm_pas_shutdown(u32 peripheral) | |
240 | { | |
241 | return __qcom_scm_pas_shutdown(peripheral); | |
242 | } | |
243 | EXPORT_SYMBOL(qcom_scm_pas_shutdown); | |
4f4066fd SV |
244 | |
245 | int qcom_scm_pil_init_image_cmd(u32 proc, u64 image_addr) | |
246 | { | |
247 | return __qcom_scm_pil_init_image_cmd(proc, image_addr); | |
248 | } | |
249 | EXPORT_SYMBOL(qcom_scm_pil_init_image_cmd); | |
250 | ||
251 | int qcom_scm_pil_mem_setup_cmd(u32 proc, u64 start_addr, u32 len) | |
252 | { | |
253 | return __qcom_scm_pil_mem_setup_cmd(proc, start_addr, len); | |
254 | } | |
255 | EXPORT_SYMBOL(qcom_scm_pil_mem_setup_cmd); | |
256 | ||
257 | int qcom_scm_pil_auth_and_reset_cmd(u32 proc) | |
258 | { | |
259 | return __qcom_scm_pil_auth_and_reset_cmd(proc); | |
260 | } | |
261 | EXPORT_SYMBOL(qcom_scm_pil_auth_and_reset_cmd); | |
262 | ||
263 | int qcom_scm_pil_shutdown_cmd(u32 proc) | |
264 | { | |
265 | return __qcom_scm_pil_shutdown_cmd(proc); | |
266 | } | |
e4b71cf0 SV |
267 | EXPORT_SYMBOL(qcom_scm_pil_shutdown_cmd); |
268 | ||
269 | int qcom_scm_iommu_dump_fault_regs(u32 id, u32 context, u64 addr, u32 len) | |
270 | { | |
271 | return __qcom_scm_iommu_dump_fault_regs(id, context, addr, len); | |
272 | } | |
273 | EXPORT_SYMBOL(qcom_scm_iommu_dump_fault_regs); | |
274 | ||
275 | int qcom_scm_iommu_set_cp_pool_size(u32 size, u32 spare) | |
276 | { | |
277 | return __qcom_scm_iommu_set_cp_pool_size(size, spare); | |
278 | } | |
279 | EXPORT_SYMBOL(qcom_scm_iommu_set_cp_pool_size); | |
280 | ||
281 | int qcom_scm_iommu_secure_ptbl_size(u32 spare, int psize[2]) | |
282 | { | |
283 | return __qcom_scm_iommu_secure_ptbl_size(spare, psize); | |
284 | } | |
285 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size); | |
286 | ||
287 | int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) | |
288 | { | |
289 | return __qcom_scm_iommu_secure_ptbl_init(addr, size, spare); | |
290 | } | |
291 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); | |
292 | ||
293 | int qcom_scm_iommu_secure_map(u64 list, u32 list_size, u32 size, | |
294 | u32 id, u32 ctx_id, u64 va, u32 info_size, | |
295 | u32 flags) | |
296 | { | |
297 | return __qcom_scm_iommu_secure_map(list, list_size, size, id, | |
298 | ctx_id, va, info_size, flags); | |
299 | } | |
300 | EXPORT_SYMBOL(qcom_scm_iommu_secure_map); | |
301 | ||
302 | int qcom_scm_iommu_secure_unmap(u32 id, u32 ctx_id, u64 va, u32 size, u32 flags) | |
303 | { | |
304 | return __qcom_scm_iommu_secure_unmap(id, ctx_id, va, size, flags); | |
305 | } | |
306 | EXPORT_SYMBOL(qcom_scm_iommu_secure_unmap); | |
307 | ||
308 | int qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) | |
309 | { | |
310 | return __qcom_scm_is_call_available(svc_id, cmd_id); | |
311 | } | |
312 | EXPORT_SYMBOL(qcom_scm_is_call_available); | |
313 | ||
314 | int qcom_scm_get_feat_version(u32 feat) | |
315 | { | |
316 | return __qcom_scm_get_feat_version(feat); | |
317 | } | |
318 | EXPORT_SYMBOL(qcom_scm_get_feat_version); | |
319 | ||
320 | int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) | |
321 | { | |
322 | return __qcom_scm_restore_sec_cfg(device_id, spare); | |
323 | } | |
324 | EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); | |
cea55f13 SV |
325 | |
326 | int qcom_scm_set_video_state(u32 state, u32 spare) | |
327 | { | |
328 | return __qcom_scm_set_video_state(state, spare); | |
329 | } | |
330 | EXPORT_SYMBOL(qcom_scm_set_video_state); | |
331 | ||
332 | int qcom_scm_mem_protect_video_var(u32 start, u32 size, | |
333 | u32 nonpixel_start, | |
334 | u32 nonpixel_size) | |
335 | { | |
336 | return __qcom_scm_mem_protect_video_var(start, size, nonpixel_start, | |
337 | nonpixel_size); | |
338 | } | |
339 | EXPORT_SYMBOL(qcom_scm_mem_protect_video_var); | |
2fa19f5a AG |
340 | /** |
341 | * qcom_scm_is_available() - Checks if SCM is available | |
342 | */ | |
343 | bool qcom_scm_is_available(void) | |
344 | { | |
345 | return !!__scm; | |
346 | } | |
347 | EXPORT_SYMBOL(qcom_scm_is_available); | |
348 | ||
95030943 SK |
349 | static int __init qcom_scm_init(void) |
350 | { | |
351 | return __qcom_scm_init(); | |
352 | } | |
353 | ||
2fa19f5a AG |
354 | static int qcom_scm_probe(struct platform_device *pdev) |
355 | { | |
356 | struct qcom_scm *scm; | |
357 | long rate; | |
358 | int ret; | |
359 | ||
95030943 SK |
360 | ret = qcom_scm_init(); |
361 | if (IS_ERR_VALUE(ret)) | |
362 | return ret; | |
363 | ||
2fa19f5a AG |
364 | scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); |
365 | if (!scm) | |
366 | return -ENOMEM; | |
367 | ||
368 | scm->core_clk = devm_clk_get(&pdev->dev, "core"); | |
369 | if (IS_ERR(scm->core_clk)) { | |
370 | if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER) | |
371 | dev_err(&pdev->dev, "failed to acquire core clk\n"); | |
49a06182 | 372 | scm->core_clk = NULL; |
2fa19f5a AG |
373 | } |
374 | ||
375 | scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); | |
376 | if (IS_ERR(scm->iface_clk)) { | |
377 | if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) | |
378 | dev_err(&pdev->dev, "failed to acquire iface clk\n"); | |
49a06182 | 379 | scm->iface_clk = NULL; |
2fa19f5a AG |
380 | } |
381 | ||
382 | scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); | |
383 | if (IS_ERR(scm->bus_clk)) { | |
384 | if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) | |
385 | dev_err(&pdev->dev, "failed to acquire bus clk\n"); | |
49a06182 SK |
386 | |
387 | scm->bus_clk = NULL; | |
2fa19f5a AG |
388 | } |
389 | ||
49a06182 | 390 | if (scm->core_clk) { |
2fa19f5a | 391 | /* vote for max clk rate for highest performance */ |
49a06182 SK |
392 | rate = clk_round_rate(scm->core_clk, INT_MAX); |
393 | ret = clk_set_rate(scm->core_clk, rate); | |
394 | if (ret) | |
395 | return ret; | |
396 | } | |
2fa19f5a AG |
397 | |
398 | __scm = scm; | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static const struct of_device_id qcom_scm_dt_match[] = { | |
404 | { .compatible = "qcom,scm",}, | |
405 | {}, | |
406 | }; | |
407 | ||
408 | MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); | |
409 | ||
410 | static struct platform_driver qcom_scm_driver = { | |
411 | .driver = { | |
412 | .name = "qcom_scm", | |
413 | .of_match_table = qcom_scm_dt_match, | |
414 | }, | |
415 | .probe = qcom_scm_probe, | |
416 | }; | |
417 | ||
418 | builtin_platform_driver(qcom_scm_driver); |