]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/firmware/qcom_scm.c
firmware: scm: make scm clks optional
[mirror_ubuntu-zesty-kernel.git] / drivers / firmware / qcom_scm.c
CommitLineData
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
25struct qcom_scm {
26 struct clk *core_clk;
27 struct clk *iface_clk;
28 struct clk *bus_clk;
29};
30
31static struct qcom_scm *__scm;
32
33static 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
57disable_iface:
49a06182
SK
58 if(__scm->iface_clk)
59 clk_disable_unprepare(__scm->iface_clk);
2fa19f5a 60disable_core:
49a06182
SK
61 if(__scm->core_clk)
62 clk_disable_unprepare(__scm->core_clk);
2fa19f5a
AG
63bail:
64 return ret;
65}
66
67static 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 */
85int 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}
89EXPORT_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 */
99int 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}
103EXPORT_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 */
113void qcom_scm_cpu_power_down(u32 flags)
114{
b6a1dfbc 115 __qcom_scm_cpu_power_down(flags);
767b0235
LI
116}
117EXPORT_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 */
124bool 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
136clk_err:
9626b699 137 return (ret > 0) ? true : false;
138}
139EXPORT_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 */
149int 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}
160EXPORT_SYMBOL(qcom_scm_hdcp_req);
ccd42027 161
b13079ce
SK
162int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp)
163{
164 return __qcom_scm_restart_proc(pid, restart, resp);
165}
166EXPORT_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 */
174bool 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}
185EXPORT_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 199int 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}
203EXPORT_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 */
214int 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}
218EXPORT_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 */
227int qcom_scm_pas_auth_and_reset(u32 peripheral)
228{
229 return __qcom_scm_pas_auth_and_reset(peripheral);
230}
231EXPORT_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 */
239int qcom_scm_pas_shutdown(u32 peripheral)
240{
241 return __qcom_scm_pas_shutdown(peripheral);
242}
243EXPORT_SYMBOL(qcom_scm_pas_shutdown);
4f4066fd
SV
244
245int qcom_scm_pil_init_image_cmd(u32 proc, u64 image_addr)
246{
247 return __qcom_scm_pil_init_image_cmd(proc, image_addr);
248}
249EXPORT_SYMBOL(qcom_scm_pil_init_image_cmd);
250
251int 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}
255EXPORT_SYMBOL(qcom_scm_pil_mem_setup_cmd);
256
257int qcom_scm_pil_auth_and_reset_cmd(u32 proc)
258{
259 return __qcom_scm_pil_auth_and_reset_cmd(proc);
260}
261EXPORT_SYMBOL(qcom_scm_pil_auth_and_reset_cmd);
262
263int qcom_scm_pil_shutdown_cmd(u32 proc)
264{
265 return __qcom_scm_pil_shutdown_cmd(proc);
266}
e4b71cf0
SV
267EXPORT_SYMBOL(qcom_scm_pil_shutdown_cmd);
268
269int 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}
273EXPORT_SYMBOL(qcom_scm_iommu_dump_fault_regs);
274
275int qcom_scm_iommu_set_cp_pool_size(u32 size, u32 spare)
276{
277 return __qcom_scm_iommu_set_cp_pool_size(size, spare);
278}
279EXPORT_SYMBOL(qcom_scm_iommu_set_cp_pool_size);
280
281int qcom_scm_iommu_secure_ptbl_size(u32 spare, int psize[2])
282{
283 return __qcom_scm_iommu_secure_ptbl_size(spare, psize);
284}
285EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size);
286
287int 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}
291EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
292
293int 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}
300EXPORT_SYMBOL(qcom_scm_iommu_secure_map);
301
302int 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}
306EXPORT_SYMBOL(qcom_scm_iommu_secure_unmap);
307
308int qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
309{
310 return __qcom_scm_is_call_available(svc_id, cmd_id);
311}
312EXPORT_SYMBOL(qcom_scm_is_call_available);
313
314int qcom_scm_get_feat_version(u32 feat)
315{
316 return __qcom_scm_get_feat_version(feat);
317}
318EXPORT_SYMBOL(qcom_scm_get_feat_version);
319
320int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
321{
322 return __qcom_scm_restore_sec_cfg(device_id, spare);
323}
324EXPORT_SYMBOL(qcom_scm_restore_sec_cfg);
cea55f13
SV
325
326int qcom_scm_set_video_state(u32 state, u32 spare)
327{
328 return __qcom_scm_set_video_state(state, spare);
329}
330EXPORT_SYMBOL(qcom_scm_set_video_state);
331
332int 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}
339EXPORT_SYMBOL(qcom_scm_mem_protect_video_var);
2fa19f5a
AG
340/**
341 * qcom_scm_is_available() - Checks if SCM is available
342 */
343bool qcom_scm_is_available(void)
344{
345 return !!__scm;
346}
347EXPORT_SYMBOL(qcom_scm_is_available);
348
95030943
SK
349static int __init qcom_scm_init(void)
350{
351 return __qcom_scm_init();
352}
353
2fa19f5a
AG
354static 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
403static const struct of_device_id qcom_scm_dt_match[] = {
404 { .compatible = "qcom,scm",},
405 {},
406};
407
408MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
409
410static 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
418builtin_platform_driver(qcom_scm_driver);