diff options
| -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 |
