diff options
author | Andy Gross <andy.gross@linaro.org> | 2016-06-03 19:25:25 -0400 |
---|---|---|
committer | Andy Gross <andy.gross@linaro.org> | 2016-06-24 14:34:00 -0400 |
commit | 16e59467a446514f971cc4669322ab387ca45155 (patch) | |
tree | 181e99eb222448a2bcffa166118b46f0f915163a /drivers/firmware | |
parent | 11bdcee4a6b9ff0b09144a81a6b903cbfa599be7 (diff) |
firmware: qcom: scm: Convert to streaming DMA APIS
This patch converts the Qualcomm SCM driver to use the streaming DMA APIs
for communication buffers. This is being done so that the
secure_flush_area call can be removed. Using the DMA APIs will also make
the SCM32 symmetric to the coming SCM64 code.
Signed-off-by: Andy Gross <andy.gross@linaro.org>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/qcom_scm-32.c | 143 | ||||
-rw-r--r-- | drivers/firmware/qcom_scm.c | 6 | ||||
-rw-r--r-- | drivers/firmware/qcom_scm.h | 10 |
3 files changed, 53 insertions, 106 deletions
diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 4388d13b437a..83a9351b9442 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c | |||
@@ -23,8 +23,7 @@ | |||
23 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/qcom_scm.h> | 25 | #include <linux/qcom_scm.h> |
26 | 26 | #include <linux/dma-mapping.h> | |
27 | #include <asm/cacheflush.h> | ||
28 | 27 | ||
29 | #include "qcom_scm.h" | 28 | #include "qcom_scm.h" |
30 | 29 | ||
@@ -97,44 +96,6 @@ struct qcom_scm_response { | |||
97 | }; | 96 | }; |
98 | 97 | ||
99 | /** | 98 | /** |
100 | * alloc_qcom_scm_command() - Allocate an SCM command | ||
101 | * @cmd_size: size of the command buffer | ||
102 | * @resp_size: size of the response buffer | ||
103 | * | ||
104 | * Allocate an SCM command, including enough room for the command | ||
105 | * and response headers as well as the command and response buffers. | ||
106 | * | ||
107 | * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails. | ||
108 | */ | ||
109 | static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) | ||
110 | { | ||
111 | struct qcom_scm_command *cmd; | ||
112 | size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + | ||
113 | resp_size; | ||
114 | u32 offset; | ||
115 | |||
116 | cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); | ||
117 | if (cmd) { | ||
118 | cmd->len = cpu_to_le32(len); | ||
119 | offset = offsetof(struct qcom_scm_command, buf); | ||
120 | cmd->buf_offset = cpu_to_le32(offset); | ||
121 | cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); | ||
122 | } | ||
123 | return cmd; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * free_qcom_scm_command() - Free an SCM command | ||
128 | * @cmd: command to free | ||
129 | * | ||
130 | * Free an SCM command. | ||
131 | */ | ||
132 | static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) | ||
133 | { | ||
134 | kfree(cmd); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response | 99 | * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response |
139 | * @cmd: command | 100 | * @cmd: command |
140 | * | 101 | * |
@@ -192,45 +153,9 @@ static u32 smc(u32 cmd_addr) | |||
192 | return r0; | 153 | return r0; |
193 | } | 154 | } |
194 | 155 | ||
195 | static int __qcom_scm_call(const struct qcom_scm_command *cmd) | ||
196 | { | ||
197 | int ret; | ||
198 | u32 cmd_addr = virt_to_phys(cmd); | ||
199 | |||
200 | /* | ||
201 | * Flush the command buffer so that the secure world sees | ||
202 | * the correct data. | ||
203 | */ | ||
204 | secure_flush_area(cmd, cmd->len); | ||
205 | |||
206 | ret = smc(cmd_addr); | ||
207 | if (ret < 0) | ||
208 | ret = qcom_scm_remap_error(ret); | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static void qcom_scm_inv_range(unsigned long start, unsigned long end) | ||
214 | { | ||
215 | u32 cacheline_size, ctr; | ||
216 | |||
217 | asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); | ||
218 | cacheline_size = 4 << ((ctr >> 16) & 0xf); | ||
219 | |||
220 | start = round_down(start, cacheline_size); | ||
221 | end = round_up(end, cacheline_size); | ||
222 | outer_inv_range(start, end); | ||
223 | while (start < end) { | ||
224 | asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) | ||
225 | : "memory"); | ||
226 | start += cacheline_size; | ||
227 | } | ||
228 | dsb(); | ||
229 | isb(); | ||
230 | } | ||
231 | |||
232 | /** | 156 | /** |
233 | * qcom_scm_call() - Send an SCM command | 157 | * qcom_scm_call() - Send an SCM command |
158 | * @dev: struct device | ||
234 | * @svc_id: service identifier | 159 | * @svc_id: service identifier |
235 | * @cmd_id: command identifier | 160 | * @cmd_id: command identifier |
236 | * @cmd_buf: command buffer | 161 | * @cmd_buf: command buffer |
@@ -247,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end) | |||
247 | * and response buffers is taken care of by qcom_scm_call; however, callers are | 172 | * and response buffers is taken care of by qcom_scm_call; however, callers are |
248 | * responsible for any other cached buffers passed over to the secure world. | 173 | * responsible for any other cached buffers passed over to the secure world. |
249 | */ | 174 | */ |
250 | static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, | 175 | static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, |
251 | size_t cmd_len, void *resp_buf, size_t resp_len) | 176 | const void *cmd_buf, size_t cmd_len, void *resp_buf, |
177 | size_t resp_len) | ||
252 | { | 178 | { |
253 | int ret; | 179 | int ret; |
254 | struct qcom_scm_command *cmd; | 180 | struct qcom_scm_command *cmd; |
255 | struct qcom_scm_response *rsp; | 181 | struct qcom_scm_response *rsp; |
256 | unsigned long start, end; | 182 | size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; |
183 | dma_addr_t cmd_phys; | ||
257 | 184 | ||
258 | cmd = alloc_qcom_scm_command(cmd_len, resp_len); | 185 | cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); |
259 | if (!cmd) | 186 | if (!cmd) |
260 | return -ENOMEM; | 187 | return -ENOMEM; |
261 | 188 | ||
189 | cmd->len = cpu_to_le32(alloc_len); | ||
190 | cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); | ||
191 | cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); | ||
192 | |||
262 | cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); | 193 | cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); |
263 | if (cmd_buf) | 194 | if (cmd_buf) |
264 | memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); | 195 | memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); |
265 | 196 | ||
197 | rsp = qcom_scm_command_to_response(cmd); | ||
198 | |||
199 | cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
200 | if (dma_mapping_error(dev, cmd_phys)) { | ||
201 | kfree(cmd); | ||
202 | return -ENOMEM; | ||
203 | } | ||
204 | |||
266 | mutex_lock(&qcom_scm_lock); | 205 | mutex_lock(&qcom_scm_lock); |
267 | ret = __qcom_scm_call(cmd); | 206 | ret = smc(cmd_phys); |
207 | if (ret < 0) | ||
208 | ret = qcom_scm_remap_error(ret); | ||
268 | mutex_unlock(&qcom_scm_lock); | 209 | mutex_unlock(&qcom_scm_lock); |
269 | if (ret) | 210 | if (ret) |
270 | goto out; | 211 | goto out; |
271 | 212 | ||
272 | rsp = qcom_scm_command_to_response(cmd); | ||
273 | start = (unsigned long)rsp; | ||
274 | |||
275 | do { | 213 | do { |
276 | qcom_scm_inv_range(start, start + sizeof(*rsp)); | 214 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, |
215 | sizeof(*rsp), DMA_FROM_DEVICE); | ||
277 | } while (!rsp->is_complete); | 216 | } while (!rsp->is_complete); |
278 | 217 | ||
279 | end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; | 218 | if (resp_buf) { |
280 | qcom_scm_inv_range(start, end); | 219 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + |
281 | 220 | le32_to_cpu(rsp->buf_offset), | |
282 | if (resp_buf) | 221 | resp_len, DMA_FROM_DEVICE); |
283 | memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); | 222 | memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), |
223 | resp_len); | ||
224 | } | ||
284 | out: | 225 | out: |
285 | free_qcom_scm_command(cmd); | 226 | dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); |
227 | kfree(cmd); | ||
286 | return ret; | 228 | return ret; |
287 | } | 229 | } |
288 | 230 | ||
@@ -437,7 +379,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | |||
437 | * Set the Linux entry point for the SCM to transfer control to when coming | 379 | * Set the Linux entry point for the SCM to transfer control to when coming |
438 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | 380 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. |
439 | */ | 381 | */ |
440 | int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | 382 | int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, |
383 | const cpumask_t *cpus) | ||
441 | { | 384 | { |
442 | int ret; | 385 | int ret; |
443 | int flags = 0; | 386 | int flags = 0; |
@@ -463,7 +406,7 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | |||
463 | 406 | ||
464 | cmd.addr = cpu_to_le32(virt_to_phys(entry)); | 407 | cmd.addr = cpu_to_le32(virt_to_phys(entry)); |
465 | cmd.flags = cpu_to_le32(flags); | 408 | cmd.flags = cpu_to_le32(flags); |
466 | ret = qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, | 409 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, |
467 | &cmd, sizeof(cmd), NULL, 0); | 410 | &cmd, sizeof(cmd), NULL, 0); |
468 | if (!ret) { | 411 | if (!ret) { |
469 | for_each_cpu(cpu, cpus) | 412 | for_each_cpu(cpu, cpus) |
@@ -487,25 +430,27 @@ void __qcom_scm_cpu_power_down(u32 flags) | |||
487 | flags & QCOM_SCM_FLUSH_FLAG_MASK); | 430 | flags & QCOM_SCM_FLUSH_FLAG_MASK); |
488 | } | 431 | } |
489 | 432 | ||
490 | int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) | 433 | int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) |
491 | { | 434 | { |
492 | int ret; | 435 | int ret; |
493 | __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); | 436 | __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); |
494 | __le32 ret_val = 0; | 437 | __le32 ret_val = 0; |
495 | 438 | ||
496 | ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd, | 439 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, |
497 | sizeof(svc_cmd), &ret_val, sizeof(ret_val)); | 440 | &svc_cmd, sizeof(svc_cmd), &ret_val, |
441 | sizeof(ret_val)); | ||
498 | if (ret) | 442 | if (ret) |
499 | return ret; | 443 | return ret; |
500 | 444 | ||
501 | return le32_to_cpu(ret_val); | 445 | return le32_to_cpu(ret_val); |
502 | } | 446 | } |
503 | 447 | ||
504 | int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | 448 | int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, |
449 | u32 req_cnt, u32 *resp) | ||
505 | { | 450 | { |
506 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | 451 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) |
507 | return -ERANGE; | 452 | return -ERANGE; |
508 | 453 | ||
509 | return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, | 454 | return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, |
510 | req, req_cnt * sizeof(*req), resp, sizeof(*resp)); | 455 | req, req_cnt * sizeof(*req), resp, sizeof(*resp)); |
511 | } | 456 | } |
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index c4ec60d220e3..937c64a78abf 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c | |||
@@ -89,7 +89,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); | |||
89 | */ | 89 | */ |
90 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | 90 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) |
91 | { | 91 | { |
92 | return __qcom_scm_set_warm_boot_addr(entry, cpus); | 92 | return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus); |
93 | } | 93 | } |
94 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); | 94 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); |
95 | 95 | ||
@@ -119,7 +119,7 @@ bool qcom_scm_hdcp_available(void) | |||
119 | if (ret) | 119 | if (ret) |
120 | return ret; | 120 | return ret; |
121 | 121 | ||
122 | ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, | 122 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, |
123 | QCOM_SCM_CMD_HDCP); | 123 | QCOM_SCM_CMD_HDCP); |
124 | 124 | ||
125 | qcom_scm_clk_disable(); | 125 | qcom_scm_clk_disable(); |
@@ -143,7 +143,7 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | |||
143 | if (ret) | 143 | if (ret) |
144 | return ret; | 144 | return ret; |
145 | 145 | ||
146 | ret = __qcom_scm_hdcp_req(req, req_cnt, resp); | 146 | ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp); |
147 | qcom_scm_clk_disable(); | 147 | qcom_scm_clk_disable(); |
148 | return ret; | 148 | return ret; |
149 | } | 149 | } |
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 7dcc73381b7a..afe6676fb396 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h | |||
@@ -19,7 +19,8 @@ | |||
19 | #define QCOM_SCM_FLAG_HLOS 0x01 | 19 | #define QCOM_SCM_FLAG_HLOS 0x01 |
20 | #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 | 20 | #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 |
21 | #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 | 21 | #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 |
22 | extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); | 22 | extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, |
23 | const cpumask_t *cpus); | ||
23 | extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); | 24 | extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); |
24 | 25 | ||
25 | #define QCOM_SCM_CMD_TERMINATE_PC 0x2 | 26 | #define QCOM_SCM_CMD_TERMINATE_PC 0x2 |
@@ -29,12 +30,13 @@ extern void __qcom_scm_cpu_power_down(u32 flags); | |||
29 | 30 | ||
30 | #define QCOM_SCM_SVC_INFO 0x6 | 31 | #define QCOM_SCM_SVC_INFO 0x6 |
31 | #define QCOM_IS_CALL_AVAIL_CMD 0x1 | 32 | #define QCOM_IS_CALL_AVAIL_CMD 0x1 |
32 | extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); | 33 | extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, |
34 | u32 cmd_id); | ||
33 | 35 | ||
34 | #define QCOM_SCM_SVC_HDCP 0x11 | 36 | #define QCOM_SCM_SVC_HDCP 0x11 |
35 | #define QCOM_SCM_CMD_HDCP 0x01 | 37 | #define QCOM_SCM_CMD_HDCP 0x01 |
36 | extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | 38 | extern int __qcom_scm_hdcp_req(struct device *dev, |
37 | u32 *resp); | 39 | struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); |
38 | 40 | ||
39 | /* common error codes */ | 41 | /* common error codes */ |
40 | #define QCOM_SCM_ENOMEM -5 | 42 | #define QCOM_SCM_ENOMEM -5 |