aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2016-07-05 01:30:11 -0400
committerOlof Johansson <olof@lixom.net>2016-07-05 01:30:11 -0400
commit7cd4837ee2febd6478ca80eb318d6d1a72f26654 (patch)
tree526b98b78ef98d22775ae6c312e03316daf41a72
parent9419491eda7daf74b189b80f80390034ddb04496 (diff)
parentdd4fe5b292226f2459305965c960d8dc39f36e0f (diff)
Merge tag 'qcom-drivers-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux into next/drivers
Qualcomm ARM Based Driver Updates for v4.8 * Rework of SCM driver * Add file patterns for Qualcomm Maintainers entry * Add worker for wcnss_ctrl signaling * Fixes for smp2p * Update smem_state properties to match documentation * Add SCM Peripheral Authentication service * Expose SCM PAS command 10 as a reset controller * tag 'qcom-drivers-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux: firmware: qcom: scm: Expose PAS command 10 as reset-controller firmware: qcom: scm: Peripheral Authentication Service soc: qcom: Update properties for smem state referencing soc: qcom: smp2p: Drop io-accessors soc: qcom: smp2p: Correct addressing of outgoing value soc: qcom: wcnss_ctrl: Make wcnss_ctrl parent the other components firmware: qcom: scm: Add support for ARM64 SoCs firmware: qcom: scm: Convert to streaming DMA APIS firmware: qcom: scm: Generalize shared error map firmware: qcom: scm: Use atomic SCM for cold boot firmware: qcom: scm: Convert SCM to platform driver MAINTAINERS: Add file patterns for qcom device tree bindings Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt4
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt4
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/firmware/Kconfig1
-rw-r--r--drivers/firmware/qcom_scm-32.c327
-rw-r--r--drivers/firmware/qcom_scm-64.c307
-rw-r--r--drivers/firmware/qcom_scm.c345
-rw-r--r--drivers/firmware/qcom_scm.h47
-rw-r--r--drivers/soc/qcom/smem_state.c12
-rw-r--r--drivers/soc/qcom/smp2p.c7
-rw-r--r--drivers/soc/qcom/smsm.c2
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c125
-rw-r--r--include/linux/qcom_scm.h8
-rw-r--r--include/linux/soc/qcom/wcnss_ctrl.h8
14 files changed, 1012 insertions, 186 deletions
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
index 5cc82b8353d8..af9ca37221ce 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
@@ -68,7 +68,7 @@ important.
68 Value type: <u32> 68 Value type: <u32>
69 Definition: must be 2 - denoting the bit in the entry and IRQ flags 69 Definition: must be 2 - denoting the bit in the entry and IRQ flags
70 70
71- #qcom,state-cells: 71- #qcom,smem-state-cells:
72 Usage: required for outgoing entries 72 Usage: required for outgoing entries
73 Value type: <u32> 73 Value type: <u32>
74 Definition: must be 1 - denoting the bit in the entry 74 Definition: must be 1 - denoting the bit in the entry
@@ -92,7 +92,7 @@ wcnss-smp2p {
92 wcnss_smp2p_out: master-kernel { 92 wcnss_smp2p_out: master-kernel {
93 qcom,entry-name = "master-kernel"; 93 qcom,entry-name = "master-kernel";
94 94
95 #qcom,state-cells = <1>; 95 #qcom,smem-state-cells = <1>;
96 }; 96 };
97 97
98 wcnss_smp2p_in: slave-kernel { 98 wcnss_smp2p_in: slave-kernel {
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
index a6634c70850d..2993b5a97dd6 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
@@ -51,7 +51,7 @@ important.
51 Definition: specifies the offset, in words, of the first bit for this 51 Definition: specifies the offset, in words, of the first bit for this
52 entry 52 entry
53 53
54- #qcom,state-cells: 54- #qcom,smem-state-cells:
55 Usage: required for local entry 55 Usage: required for local entry
56 Value type: <u32> 56 Value type: <u32>
57 Definition: must be 1 - denotes bit number 57 Definition: must be 1 - denotes bit number
@@ -91,7 +91,7 @@ smsm {
91 apps_smsm: apps@0 { 91 apps_smsm: apps@0 {
92 reg = <0>; 92 reg = <0>;
93 93
94 #qcom,state-cells = <1>; 94 #qcom,smem-state-cells = <1>;
95 }; 95 };
96 96
97 wcnss_smsm: wcnss@7 { 97 wcnss_smsm: wcnss@7 {
diff --git a/MAINTAINERS b/MAINTAINERS
index 2e0187d3a53b..d5c45bce85d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1521,6 +1521,7 @@ M: David Brown <david.brown@linaro.org>
1521L: linux-arm-msm@vger.kernel.org 1521L: linux-arm-msm@vger.kernel.org
1522L: linux-soc@vger.kernel.org 1522L: linux-soc@vger.kernel.org
1523S: Maintained 1523S: Maintained
1524F: Documentation/devicetree/bindings/soc/qcom/
1524F: arch/arm/boot/dts/qcom-*.dts 1525F: arch/arm/boot/dts/qcom-*.dts
1525F: arch/arm/boot/dts/qcom-*.dtsi 1526F: arch/arm/boot/dts/qcom-*.dtsi
1526F: arch/arm/mach-qcom/ 1527F: arch/arm/mach-qcom/
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 4683bc375e96..541d3fb7ae43 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -194,6 +194,7 @@ config FW_CFG_SYSFS_CMDLINE
194config QCOM_SCM 194config QCOM_SCM
195 bool 195 bool
196 depends on ARM || ARM64 196 depends on ARM || ARM64
197 select RESET_CONTROLLER
197 198
198config QCOM_SCM_32 199config QCOM_SCM_32
199 def_bool y 200 def_bool y
diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c
index 0883292f640f..c6aeedbdcbb0 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 */
109static 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 */
132static 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 *
@@ -168,23 +129,6 @@ static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response
168 return (void *)rsp + le32_to_cpu(rsp->buf_offset); 129 return (void *)rsp + le32_to_cpu(rsp->buf_offset);
169} 130}
170 131
171static int qcom_scm_remap_error(int err)
172{
173 pr_err("qcom_scm_call failed with error code %d\n", err);
174 switch (err) {
175 case QCOM_SCM_ERROR:
176 return -EIO;
177 case QCOM_SCM_EINVAL_ADDR:
178 case QCOM_SCM_EINVAL_ARG:
179 return -EINVAL;
180 case QCOM_SCM_EOPNOTSUPP:
181 return -EOPNOTSUPP;
182 case QCOM_SCM_ENOMEM:
183 return -ENOMEM;
184 }
185 return -EINVAL;
186}
187
188static u32 smc(u32 cmd_addr) 132static u32 smc(u32 cmd_addr)
189{ 133{
190 int context_id; 134 int context_id;
@@ -209,45 +153,9 @@ static u32 smc(u32 cmd_addr)
209 return r0; 153 return r0;
210} 154}
211 155
212static int __qcom_scm_call(const struct qcom_scm_command *cmd)
213{
214 int ret;
215 u32 cmd_addr = virt_to_phys(cmd);
216
217 /*
218 * Flush the command buffer so that the secure world sees
219 * the correct data.
220 */
221 secure_flush_area(cmd, cmd->len);
222
223 ret = smc(cmd_addr);
224 if (ret < 0)
225 ret = qcom_scm_remap_error(ret);
226
227 return ret;
228}
229
230static void qcom_scm_inv_range(unsigned long start, unsigned long end)
231{
232 u32 cacheline_size, ctr;
233
234 asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
235 cacheline_size = 4 << ((ctr >> 16) & 0xf);
236
237 start = round_down(start, cacheline_size);
238 end = round_up(end, cacheline_size);
239 outer_inv_range(start, end);
240 while (start < end) {
241 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
242 : "memory");
243 start += cacheline_size;
244 }
245 dsb();
246 isb();
247}
248
249/** 156/**
250 * qcom_scm_call() - Send an SCM command 157 * qcom_scm_call() - Send an SCM command
158 * @dev: struct device
251 * @svc_id: service identifier 159 * @svc_id: service identifier
252 * @cmd_id: command identifier 160 * @cmd_id: command identifier
253 * @cmd_buf: command buffer 161 * @cmd_buf: command buffer
@@ -264,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end)
264 * 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
265 * 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.
266 */ 174 */
267static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, 175static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
268 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)
269{ 178{
270 int ret; 179 int ret;
271 struct qcom_scm_command *cmd; 180 struct qcom_scm_command *cmd;
272 struct qcom_scm_response *rsp; 181 struct qcom_scm_response *rsp;
273 unsigned long start, end; 182 size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
183 dma_addr_t cmd_phys;
274 184
275 cmd = alloc_qcom_scm_command(cmd_len, resp_len); 185 cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
276 if (!cmd) 186 if (!cmd)
277 return -ENOMEM; 187 return -ENOMEM;
278 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
279 cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); 193 cmd->id = cpu_to_le32((svc_id << 10) | cmd_id);
280 if (cmd_buf) 194 if (cmd_buf)
281 memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); 195 memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len);
282 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
283 mutex_lock(&qcom_scm_lock); 205 mutex_lock(&qcom_scm_lock);
284 ret = __qcom_scm_call(cmd); 206 ret = smc(cmd_phys);
207 if (ret < 0)
208 ret = qcom_scm_remap_error(ret);
285 mutex_unlock(&qcom_scm_lock); 209 mutex_unlock(&qcom_scm_lock);
286 if (ret) 210 if (ret)
287 goto out; 211 goto out;
288 212
289 rsp = qcom_scm_command_to_response(cmd);
290 start = (unsigned long)rsp;
291
292 do { 213 do {
293 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);
294 } while (!rsp->is_complete); 216 } while (!rsp->is_complete);
295 217
296 end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; 218 if (resp_buf) {
297 qcom_scm_inv_range(start, end); 219 dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
298 220 le32_to_cpu(rsp->buf_offset),
299 if (resp_buf) 221 resp_len, DMA_FROM_DEVICE);
300 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 }
301out: 225out:
302 free_qcom_scm_command(cmd); 226 dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
227 kfree(cmd);
303 return ret; 228 return ret;
304} 229}
305 230
@@ -342,6 +267,41 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
342 return r0; 267 return r0;
343} 268}
344 269
270/**
271 * qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments
272 * @svc_id: service identifier
273 * @cmd_id: command identifier
274 * @arg1: first argument
275 * @arg2: second argument
276 *
277 * This shall only be used with commands that are guaranteed to be
278 * uninterruptable, atomic and SMP safe.
279 */
280static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
281{
282 int context_id;
283
284 register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2);
285 register u32 r1 asm("r1") = (u32)&context_id;
286 register u32 r2 asm("r2") = arg1;
287 register u32 r3 asm("r3") = arg2;
288
289 asm volatile(
290 __asmeq("%0", "r0")
291 __asmeq("%1", "r0")
292 __asmeq("%2", "r1")
293 __asmeq("%3", "r2")
294 __asmeq("%4", "r3")
295#ifdef REQUIRES_SEC
296 ".arch_extension sec\n"
297#endif
298 "smc #0 @ switch to secure world\n"
299 : "=r" (r0)
300 : "r" (r0), "r" (r1), "r" (r2), "r" (r3)
301 );
302 return r0;
303}
304
345u32 qcom_scm_get_version(void) 305u32 qcom_scm_get_version(void)
346{ 306{
347 int context_id; 307 int context_id;
@@ -378,22 +338,6 @@ u32 qcom_scm_get_version(void)
378} 338}
379EXPORT_SYMBOL(qcom_scm_get_version); 339EXPORT_SYMBOL(qcom_scm_get_version);
380 340
381/*
382 * Set the cold/warm boot address for one of the CPU cores.
383 */
384static int qcom_scm_set_boot_addr(u32 addr, int flags)
385{
386 struct {
387 __le32 flags;
388 __le32 addr;
389 } cmd;
390
391 cmd.addr = cpu_to_le32(addr);
392 cmd.flags = cpu_to_le32(flags);
393 return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
394 &cmd, sizeof(cmd), NULL, 0);
395}
396
397/** 341/**
398 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus 342 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
399 * @entry: Entry point function for the cpus 343 * @entry: Entry point function for the cpus
@@ -423,7 +367,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
423 set_cpu_present(cpu, false); 367 set_cpu_present(cpu, false);
424 } 368 }
425 369
426 return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); 370 return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
371 flags, virt_to_phys(entry));
427} 372}
428 373
429/** 374/**
@@ -434,11 +379,16 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
434 * 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
435 * 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.
436 */ 381 */
437int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) 382int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
383 const cpumask_t *cpus)
438{ 384{
439 int ret; 385 int ret;
440 int flags = 0; 386 int flags = 0;
441 int cpu; 387 int cpu;
388 struct {
389 __le32 flags;
390 __le32 addr;
391 } cmd;
442 392
443 /* 393 /*
444 * Reassign only if we are switching from hotplug entry point 394 * Reassign only if we are switching from hotplug entry point
@@ -454,7 +404,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
454 if (!flags) 404 if (!flags)
455 return 0; 405 return 0;
456 406
457 ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags); 407 cmd.addr = cpu_to_le32(virt_to_phys(entry));
408 cmd.flags = cpu_to_le32(flags);
409 ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
410 &cmd, sizeof(cmd), NULL, 0);
458 if (!ret) { 411 if (!ret) {
459 for_each_cpu(cpu, cpus) 412 for_each_cpu(cpu, cpus)
460 qcom_scm_wb[cpu].entry = entry; 413 qcom_scm_wb[cpu].entry = entry;
@@ -477,25 +430,133 @@ void __qcom_scm_cpu_power_down(u32 flags)
477 flags & QCOM_SCM_FLUSH_FLAG_MASK); 430 flags & QCOM_SCM_FLUSH_FLAG_MASK);
478} 431}
479 432
480int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) 433int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
481{ 434{
482 int ret; 435 int ret;
483 __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); 436 __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id);
484 __le32 ret_val = 0; 437 __le32 ret_val = 0;
485 438
486 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,
487 sizeof(svc_cmd), &ret_val, sizeof(ret_val)); 440 &svc_cmd, sizeof(svc_cmd), &ret_val,
441 sizeof(ret_val));
488 if (ret) 442 if (ret)
489 return ret; 443 return ret;
490 444
491 return le32_to_cpu(ret_val); 445 return le32_to_cpu(ret_val);
492} 446}
493 447
494int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) 448int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
449 u32 req_cnt, u32 *resp)
495{ 450{
496 if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) 451 if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
497 return -ERANGE; 452 return -ERANGE;
498 453
499 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,
500 req, req_cnt * sizeof(*req), resp, sizeof(*resp)); 455 req, req_cnt * sizeof(*req), resp, sizeof(*resp));
501} 456}
457
458void __qcom_scm_init(void)
459{
460}
461
462bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
463{
464 __le32 out;
465 __le32 in;
466 int ret;
467
468 in = cpu_to_le32(peripheral);
469 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
470 QCOM_SCM_PAS_IS_SUPPORTED_CMD,
471 &in, sizeof(in),
472 &out, sizeof(out));
473
474 return ret ? false : !!out;
475}
476
477int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
478 dma_addr_t metadata_phys)
479{
480 __le32 scm_ret;
481 int ret;
482 struct {
483 __le32 proc;
484 __le32 image_addr;
485 } request;
486
487 request.proc = cpu_to_le32(peripheral);
488 request.image_addr = cpu_to_le32(metadata_phys);
489
490 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
491 QCOM_SCM_PAS_INIT_IMAGE_CMD,
492 &request, sizeof(request),
493 &scm_ret, sizeof(scm_ret));
494
495 return ret ? : le32_to_cpu(scm_ret);
496}
497
498int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
499 phys_addr_t addr, phys_addr_t size)
500{
501 __le32 scm_ret;
502 int ret;
503 struct {
504 __le32 proc;
505 __le32 addr;
506 __le32 len;
507 } request;
508
509 request.proc = cpu_to_le32(peripheral);
510 request.addr = cpu_to_le32(addr);
511 request.len = cpu_to_le32(size);
512
513 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
514 QCOM_SCM_PAS_MEM_SETUP_CMD,
515 &request, sizeof(request),
516 &scm_ret, sizeof(scm_ret));
517
518 return ret ? : le32_to_cpu(scm_ret);
519}
520
521int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
522{
523 __le32 out;
524 __le32 in;
525 int ret;
526
527 in = cpu_to_le32(peripheral);
528 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
529 QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
530 &in, sizeof(in),
531 &out, sizeof(out));
532
533 return ret ? : le32_to_cpu(out);
534}
535
536int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
537{
538 __le32 out;
539 __le32 in;
540 int ret;
541
542 in = cpu_to_le32(peripheral);
543 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
544 QCOM_SCM_PAS_SHUTDOWN_CMD,
545 &in, sizeof(in),
546 &out, sizeof(out));
547
548 return ret ? : le32_to_cpu(out);
549}
550
551int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
552{
553 __le32 out;
554 __le32 in = cpu_to_le32(reset);
555 int ret;
556
557 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET,
558 &in, sizeof(in),
559 &out, sizeof(out));
560
561 return ret ? : le32_to_cpu(out);
562}
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index bb6555f6d63b..4a0f5ead4fb5 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -12,7 +12,150 @@
12 12
13#include <linux/io.h> 13#include <linux/io.h>
14#include <linux/errno.h> 14#include <linux/errno.h>
15#include <linux/delay.h>
16#include <linux/mutex.h>
17#include <linux/slab.h>
18#include <linux/types.h>
15#include <linux/qcom_scm.h> 19#include <linux/qcom_scm.h>
20#include <linux/arm-smccc.h>
21#include <linux/dma-mapping.h>
22
23#include "qcom_scm.h"
24
25#define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
26
27#define MAX_QCOM_SCM_ARGS 10
28#define MAX_QCOM_SCM_RETS 3
29
30enum qcom_scm_arg_types {
31 QCOM_SCM_VAL,
32 QCOM_SCM_RO,
33 QCOM_SCM_RW,
34 QCOM_SCM_BUFVAL,
35};
36
37#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\
38 (((a) & 0x3) << 4) | \
39 (((b) & 0x3) << 6) | \
40 (((c) & 0x3) << 8) | \
41 (((d) & 0x3) << 10) | \
42 (((e) & 0x3) << 12) | \
43 (((f) & 0x3) << 14) | \
44 (((g) & 0x3) << 16) | \
45 (((h) & 0x3) << 18) | \
46 (((i) & 0x3) << 20) | \
47 (((j) & 0x3) << 22) | \
48 ((num) & 0xf))
49
50#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
51
52/**
53 * struct qcom_scm_desc
54 * @arginfo: Metadata describing the arguments in args[]
55 * @args: The array of arguments for the secure syscall
56 * @res: The values returned by the secure syscall
57 */
58struct qcom_scm_desc {
59 u32 arginfo;
60 u64 args[MAX_QCOM_SCM_ARGS];
61};
62
63static u64 qcom_smccc_convention = -1;
64static DEFINE_MUTEX(qcom_scm_lock);
65
66#define QCOM_SCM_EBUSY_WAIT_MS 30
67#define QCOM_SCM_EBUSY_MAX_RETRY 20
68
69#define N_EXT_QCOM_SCM_ARGS 7
70#define FIRST_EXT_ARG_IDX 3
71#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1)
72
73/**
74 * qcom_scm_call() - Invoke a syscall in the secure world
75 * @dev: device
76 * @svc_id: service identifier
77 * @cmd_id: command identifier
78 * @desc: Descriptor structure containing arguments and return values
79 *
80 * Sends a command to the SCM and waits for the command to finish processing.
81 * This should *only* be called in pre-emptible context.
82*/
83static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
84 const struct qcom_scm_desc *desc,
85 struct arm_smccc_res *res)
86{
87 int arglen = desc->arginfo & 0xf;
88 int retry_count = 0, i;
89 u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id);
90 u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX];
91 dma_addr_t args_phys = 0;
92 void *args_virt = NULL;
93 size_t alloc_len;
94
95 if (unlikely(arglen > N_REGISTER_ARGS)) {
96 alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
97 args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
98
99 if (!args_virt)
100 return -ENOMEM;
101
102 if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
103 __le32 *args = args_virt;
104
105 for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
106 args[i] = cpu_to_le32(desc->args[i +
107 FIRST_EXT_ARG_IDX]);
108 } else {
109 __le64 *args = args_virt;
110
111 for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
112 args[i] = cpu_to_le64(desc->args[i +
113 FIRST_EXT_ARG_IDX]);
114 }
115
116 args_phys = dma_map_single(dev, args_virt, alloc_len,
117 DMA_TO_DEVICE);
118
119 if (dma_mapping_error(dev, args_phys)) {
120 kfree(args_virt);
121 return -ENOMEM;
122 }
123
124 x5 = args_phys;
125 }
126
127 do {
128 mutex_lock(&qcom_scm_lock);
129
130 cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
131 qcom_smccc_convention,
132 ARM_SMCCC_OWNER_SIP, fn_id);
133
134 do {
135 arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
136 desc->args[1], desc->args[2], x5, 0, 0,
137 res);
138 } while (res->a0 == QCOM_SCM_INTERRUPTED);
139
140 mutex_unlock(&qcom_scm_lock);
141
142 if (res->a0 == QCOM_SCM_V2_EBUSY) {
143 if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
144 break;
145 msleep(QCOM_SCM_EBUSY_WAIT_MS);
146 }
147 } while (res->a0 == QCOM_SCM_V2_EBUSY);
148
149 if (args_virt) {
150 dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
151 kfree(args_virt);
152 }
153
154 if (res->a0 < 0)
155 return qcom_scm_remap_error(res->a0);
156
157 return 0;
158}
16 159
17/** 160/**
18 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus 161 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
@@ -29,13 +172,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
29 172
30/** 173/**
31 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus 174 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
175 * @dev: Device pointer
32 * @entry: Entry point function for the cpus 176 * @entry: Entry point function for the cpus
33 * @cpus: The cpumask of cpus that will use the entry point 177 * @cpus: The cpumask of cpus that will use the entry point
34 * 178 *
35 * Set the Linux entry point for the SCM to transfer control to when coming 179 * Set the Linux entry point for the SCM to transfer control to when coming
36 * out of a power down. CPU power down may be executed on cpuidle or hotplug. 180 * out of a power down. CPU power down may be executed on cpuidle or hotplug.
37 */ 181 */
38int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) 182int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
183 const cpumask_t *cpus)
39{ 184{
40 return -ENOTSUPP; 185 return -ENOTSUPP;
41} 186}
@@ -52,12 +197,164 @@ void __qcom_scm_cpu_power_down(u32 flags)
52{ 197{
53} 198}
54 199
55int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) 200int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
56{ 201{
57 return -ENOTSUPP; 202 int ret;
203 struct qcom_scm_desc desc = {0};
204 struct arm_smccc_res res;
205
206 desc.arginfo = QCOM_SCM_ARGS(1);
207 desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) |
208 (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
209
210 ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD,
211 &desc, &res);
212
213 return ret ? : res.a1;
58} 214}
59 215
60int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) 216int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
217 u32 req_cnt, u32 *resp)
61{ 218{
62 return -ENOTSUPP; 219 int ret;
220 struct qcom_scm_desc desc = {0};
221 struct arm_smccc_res res;
222
223 if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
224 return -ERANGE;
225
226 desc.args[0] = req[0].addr;
227 desc.args[1] = req[0].val;
228 desc.args[2] = req[1].addr;
229 desc.args[3] = req[1].val;
230 desc.args[4] = req[2].addr;
231 desc.args[5] = req[2].val;
232 desc.args[6] = req[3].addr;
233 desc.args[7] = req[3].val;
234 desc.args[8] = req[4].addr;
235 desc.args[9] = req[4].val;
236 desc.arginfo = QCOM_SCM_ARGS(10);
237
238 ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc,
239 &res);
240 *resp = res.a1;
241
242 return ret;
243}
244
245void __qcom_scm_init(void)
246{
247 u64 cmd;
248 struct arm_smccc_res res;
249 u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD);
250
251 /* First try a SMC64 call */
252 cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
253 ARM_SMCCC_OWNER_SIP, function);
254
255 arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)),
256 0, 0, 0, 0, 0, &res);
257
258 if (!res.a0 && res.a1)
259 qcom_smccc_convention = ARM_SMCCC_SMC_64;
260 else
261 qcom_smccc_convention = ARM_SMCCC_SMC_32;
262}
263
264bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
265{
266 int ret;
267 struct qcom_scm_desc desc = {0};
268 struct arm_smccc_res res;
269
270 desc.args[0] = peripheral;
271 desc.arginfo = QCOM_SCM_ARGS(1);
272
273 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
274 QCOM_SCM_PAS_IS_SUPPORTED_CMD,
275 &desc, &res);
276
277 return ret ? false : !!res.a1;
278}
279
280int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
281 dma_addr_t metadata_phys)
282{
283 int ret;
284 struct qcom_scm_desc desc = {0};
285 struct arm_smccc_res res;
286
287 desc.args[0] = peripheral;
288 desc.args[1] = metadata_phys;
289 desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
290
291 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
292 &desc, &res);
293
294 return ret ? : res.a1;
295}
296
297int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
298 phys_addr_t addr, phys_addr_t size)
299{
300 int ret;
301 struct qcom_scm_desc desc = {0};
302 struct arm_smccc_res res;
303
304 desc.args[0] = peripheral;
305 desc.args[1] = addr;
306 desc.args[2] = size;
307 desc.arginfo = QCOM_SCM_ARGS(3);
308
309 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
310 &desc, &res);
311
312 return ret ? : res.a1;
313}
314
315int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
316{
317 int ret;
318 struct qcom_scm_desc desc = {0};
319 struct arm_smccc_res res;
320
321 desc.args[0] = peripheral;
322 desc.arginfo = QCOM_SCM_ARGS(1);
323
324 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
325 QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
326 &desc, &res);
327
328 return ret ? : res.a1;
329}
330
331int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
332{
333 int ret;
334 struct qcom_scm_desc desc = {0};
335 struct arm_smccc_res res;
336
337 desc.args[0] = peripheral;
338 desc.arginfo = QCOM_SCM_ARGS(1);
339
340 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD,
341 &desc, &res);
342
343 return ret ? : res.a1;
344}
345
346int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
347{
348 struct qcom_scm_desc desc = {0};
349 struct arm_smccc_res res;
350 int ret;
351
352 desc.args[0] = reset;
353 desc.args[1] = 0;
354 desc.arginfo = QCOM_SCM_ARGS(2);
355
356 ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc,
357 &res);
358
359 return ret ? : res.a1;
63} 360}
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 45c008d68891..84330c5f05d0 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -10,19 +10,64 @@
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details. 11 * GNU General Public License for more details.
12 * 12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301, USA.
17 */ 13 */
18 14#include <linux/platform_device.h>
15#include <linux/module.h>
19#include <linux/cpumask.h> 16#include <linux/cpumask.h>
20#include <linux/export.h> 17#include <linux/export.h>
18#include <linux/dma-mapping.h>
21#include <linux/types.h> 19#include <linux/types.h>
22#include <linux/qcom_scm.h> 20#include <linux/qcom_scm.h>
21#include <linux/of.h>
22#include <linux/of_platform.h>
23#include <linux/clk.h>
24#include <linux/reset-controller.h>
23 25
24#include "qcom_scm.h" 26#include "qcom_scm.h"
25 27
28struct qcom_scm {
29 struct device *dev;
30 struct clk *core_clk;
31 struct clk *iface_clk;
32 struct clk *bus_clk;
33 struct reset_controller_dev reset;
34};
35
36static struct qcom_scm *__scm;
37
38static int qcom_scm_clk_enable(void)
39{
40 int ret;
41
42 ret = clk_prepare_enable(__scm->core_clk);
43 if (ret)
44 goto bail;
45
46 ret = clk_prepare_enable(__scm->iface_clk);
47 if (ret)
48 goto disable_core;
49
50 ret = clk_prepare_enable(__scm->bus_clk);
51 if (ret)
52 goto disable_iface;
53
54 return 0;
55
56disable_iface:
57 clk_disable_unprepare(__scm->iface_clk);
58disable_core:
59 clk_disable_unprepare(__scm->core_clk);
60bail:
61 return ret;
62}
63
64static void qcom_scm_clk_disable(void)
65{
66 clk_disable_unprepare(__scm->core_clk);
67 clk_disable_unprepare(__scm->iface_clk);
68 clk_disable_unprepare(__scm->bus_clk);
69}
70
26/** 71/**
27 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus 72 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
28 * @entry: Entry point function for the cpus 73 * @entry: Entry point function for the cpus
@@ -47,7 +92,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
47 */ 92 */
48int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) 93int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
49{ 94{
50 return __qcom_scm_set_warm_boot_addr(entry, cpus); 95 return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
51} 96}
52EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); 97EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
53 98
@@ -72,12 +117,17 @@ EXPORT_SYMBOL(qcom_scm_cpu_power_down);
72 */ 117 */
73bool qcom_scm_hdcp_available(void) 118bool qcom_scm_hdcp_available(void)
74{ 119{
75 int ret; 120 int ret = qcom_scm_clk_enable();
121
122 if (ret)
123 return ret;
76 124
77 ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, 125 ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
78 QCOM_SCM_CMD_HDCP); 126 QCOM_SCM_CMD_HDCP);
79 127
80 return (ret > 0) ? true : false; 128 qcom_scm_clk_disable();
129
130 return ret > 0 ? true : false;
81} 131}
82EXPORT_SYMBOL(qcom_scm_hdcp_available); 132EXPORT_SYMBOL(qcom_scm_hdcp_available);
83 133
@@ -91,6 +141,279 @@ EXPORT_SYMBOL(qcom_scm_hdcp_available);
91 */ 141 */
92int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) 142int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
93{ 143{
94 return __qcom_scm_hdcp_req(req, req_cnt, resp); 144 int ret = qcom_scm_clk_enable();
145
146 if (ret)
147 return ret;
148
149 ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
150 qcom_scm_clk_disable();
151 return ret;
95} 152}
96EXPORT_SYMBOL(qcom_scm_hdcp_req); 153EXPORT_SYMBOL(qcom_scm_hdcp_req);
154
155/**
156 * qcom_scm_pas_supported() - Check if the peripheral authentication service is
157 * available for the given peripherial
158 * @peripheral: peripheral id
159 *
160 * Returns true if PAS is supported for this peripheral, otherwise false.
161 */
162bool qcom_scm_pas_supported(u32 peripheral)
163{
164 int ret;
165
166 ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
167 QCOM_SCM_PAS_IS_SUPPORTED_CMD);
168 if (ret <= 0)
169 return false;
170
171 return __qcom_scm_pas_supported(__scm->dev, peripheral);
172}
173EXPORT_SYMBOL(qcom_scm_pas_supported);
174
175/**
176 * qcom_scm_pas_init_image() - Initialize peripheral authentication service
177 * state machine for a given peripheral, using the
178 * metadata
179 * @peripheral: peripheral id
180 * @metadata: pointer to memory containing ELF header, program header table
181 * and optional blob of data used for authenticating the metadata
182 * and the rest of the firmware
183 * @size: size of the metadata
184 *
185 * Returns 0 on success.
186 */
187int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
188{
189 dma_addr_t mdata_phys;
190 void *mdata_buf;
191 int ret;
192
193 /*
194 * During the scm call memory protection will be enabled for the meta
195 * data blob, so make sure it's physically contiguous, 4K aligned and
196 * non-cachable to avoid XPU violations.
197 */
198 mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
199 GFP_KERNEL);
200 if (!mdata_buf) {
201 dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
202 return -ENOMEM;
203 }
204 memcpy(mdata_buf, metadata, size);
205
206 ret = qcom_scm_clk_enable();
207 if (ret)
208 goto free_metadata;
209
210 ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
211
212 qcom_scm_clk_disable();
213
214free_metadata:
215 dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
216
217 return ret;
218}
219EXPORT_SYMBOL(qcom_scm_pas_init_image);
220
221/**
222 * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
223 * for firmware loading
224 * @peripheral: peripheral id
225 * @addr: start address of memory area to prepare
226 * @size: size of the memory area to prepare
227 *
228 * Returns 0 on success.
229 */
230int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
231{
232 int ret;
233
234 ret = qcom_scm_clk_enable();
235 if (ret)
236 return ret;
237
238 ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
239 qcom_scm_clk_disable();
240
241 return ret;
242}
243EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
244
245/**
246 * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
247 * and reset the remote processor
248 * @peripheral: peripheral id
249 *
250 * Return 0 on success.
251 */
252int qcom_scm_pas_auth_and_reset(u32 peripheral)
253{
254 int ret;
255
256 ret = qcom_scm_clk_enable();
257 if (ret)
258 return ret;
259
260 ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
261 qcom_scm_clk_disable();
262
263 return ret;
264}
265EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
266
267/**
268 * qcom_scm_pas_shutdown() - Shut down the remote processor
269 * @peripheral: peripheral id
270 *
271 * Returns 0 on success.
272 */
273int qcom_scm_pas_shutdown(u32 peripheral)
274{
275 int ret;
276
277 ret = qcom_scm_clk_enable();
278 if (ret)
279 return ret;
280
281 ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
282 qcom_scm_clk_disable();
283
284 return ret;
285}
286EXPORT_SYMBOL(qcom_scm_pas_shutdown);
287
288static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
289 unsigned long idx)
290{
291 if (idx != 0)
292 return -EINVAL;
293
294 return __qcom_scm_pas_mss_reset(__scm->dev, 1);
295}
296
297static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
298 unsigned long idx)
299{
300 if (idx != 0)
301 return -EINVAL;
302
303 return __qcom_scm_pas_mss_reset(__scm->dev, 0);
304}
305
306static const struct reset_control_ops qcom_scm_pas_reset_ops = {
307 .assert = qcom_scm_pas_reset_assert,
308 .deassert = qcom_scm_pas_reset_deassert,
309};
310
311
312static int qcom_scm_probe(struct platform_device *pdev)
313{
314 struct qcom_scm *scm;
315 int ret;
316
317 scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
318 if (!scm)
319 return -ENOMEM;
320
321 scm->core_clk = devm_clk_get(&pdev->dev, "core");
322 if (IS_ERR(scm->core_clk)) {
323 if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
324 return PTR_ERR(scm->core_clk);
325
326 scm->core_clk = NULL;
327 }
328
329 if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) {
330 scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
331 if (IS_ERR(scm->iface_clk)) {
332 if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
333 dev_err(&pdev->dev, "failed to acquire iface clk\n");
334 return PTR_ERR(scm->iface_clk);
335 }
336
337 scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
338 if (IS_ERR(scm->bus_clk)) {
339 if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
340 dev_err(&pdev->dev, "failed to acquire bus clk\n");
341 return PTR_ERR(scm->bus_clk);
342 }
343 }
344
345 scm->reset.ops = &qcom_scm_pas_reset_ops;
346 scm->reset.nr_resets = 1;
347 scm->reset.of_node = pdev->dev.of_node;
348 reset_controller_register(&scm->reset);
349
350 /* vote for max clk rate for highest performance */
351 ret = clk_set_rate(scm->core_clk, INT_MAX);
352 if (ret)
353 return ret;
354
355 __scm = scm;
356 __scm->dev = &pdev->dev;
357
358 __qcom_scm_init();
359
360 return 0;
361}
362
363static const struct of_device_id qcom_scm_dt_match[] = {
364 { .compatible = "qcom,scm-apq8064",},
365 { .compatible = "qcom,scm-msm8660",},
366 { .compatible = "qcom,scm-msm8960",},
367 { .compatible = "qcom,scm",},
368 {}
369};
370
371MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
372
373static struct platform_driver qcom_scm_driver = {
374 .driver = {
375 .name = "qcom_scm",
376 .of_match_table = qcom_scm_dt_match,
377 },
378 .probe = qcom_scm_probe,
379};
380
381static int __init qcom_scm_init(void)
382{
383 struct device_node *np, *fw_np;
384 int ret;
385
386 fw_np = of_find_node_by_name(NULL, "firmware");
387
388 if (!fw_np)
389 return -ENODEV;
390
391 np = of_find_matching_node(fw_np, qcom_scm_dt_match);
392
393 if (!np) {
394 of_node_put(fw_np);
395 return -ENODEV;
396 }
397
398 of_node_put(np);
399
400 ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
401
402 of_node_put(fw_np);
403
404 if (ret)
405 return ret;
406
407 return platform_driver_register(&qcom_scm_driver);
408}
409
410arch_initcall(qcom_scm_init);
411
412static void __exit qcom_scm_exit(void)
413{
414 platform_driver_unregister(&qcom_scm_driver);
415}
416module_exit(qcom_scm_exit);
417
418MODULE_DESCRIPTION("Qualcomm SCM driver");
419MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index 2cce75c08b99..3584b00fe7e6 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
22extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); 22extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
23 const cpumask_t *cpus);
23extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); 24extern 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,14 +30,34 @@ 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
32extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); 33extern 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
36extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, 38extern int __qcom_scm_hdcp_req(struct device *dev,
37 u32 *resp); 39 struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
40
41extern void __qcom_scm_init(void);
42
43#define QCOM_SCM_SVC_PIL 0x2
44#define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1
45#define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2
46#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5
47#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6
48#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7
49#define QCOM_SCM_PAS_MSS_RESET 0xa
50extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral);
51extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
52 dma_addr_t metadata_phys);
53extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
54 phys_addr_t addr, phys_addr_t size);
55extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral);
56extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral);
57extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset);
38 58
39/* common error codes */ 59/* common error codes */
60#define QCOM_SCM_V2_EBUSY -12
40#define QCOM_SCM_ENOMEM -5 61#define QCOM_SCM_ENOMEM -5
41#define QCOM_SCM_EOPNOTSUPP -4 62#define QCOM_SCM_EOPNOTSUPP -4
42#define QCOM_SCM_EINVAL_ADDR -3 63#define QCOM_SCM_EINVAL_ADDR -3
@@ -44,4 +65,22 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
44#define QCOM_SCM_ERROR -1 65#define QCOM_SCM_ERROR -1
45#define QCOM_SCM_INTERRUPTED 1 66#define QCOM_SCM_INTERRUPTED 1
46 67
68static inline int qcom_scm_remap_error(int err)
69{
70 switch (err) {
71 case QCOM_SCM_ERROR:
72 return -EIO;
73 case QCOM_SCM_EINVAL_ADDR:
74 case QCOM_SCM_EINVAL_ARG:
75 return -EINVAL;
76 case QCOM_SCM_EOPNOTSUPP:
77 return -EOPNOTSUPP;
78 case QCOM_SCM_ENOMEM:
79 return -ENOMEM;
80 case QCOM_SCM_V2_EBUSY:
81 return -EBUSY;
82 }
83 return -EINVAL;
84}
85
47#endif 86#endif
diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c
index 54261decb369..d5437ca76ed9 100644
--- a/drivers/soc/qcom/smem_state.c
+++ b/drivers/soc/qcom/smem_state.c
@@ -104,26 +104,26 @@ struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
104 104
105 if (con_id) { 105 if (con_id) {
106 index = of_property_match_string(dev->of_node, 106 index = of_property_match_string(dev->of_node,
107 "qcom,state-names", 107 "qcom,smem-state-names",
108 con_id); 108 con_id);
109 if (index < 0) { 109 if (index < 0) {
110 dev_err(dev, "missing qcom,state-names\n"); 110 dev_err(dev, "missing qcom,smem-state-names\n");
111 return ERR_PTR(index); 111 return ERR_PTR(index);
112 } 112 }
113 } 113 }
114 114
115 ret = of_parse_phandle_with_args(dev->of_node, 115 ret = of_parse_phandle_with_args(dev->of_node,
116 "qcom,state", 116 "qcom,smem-states",
117 "#qcom,state-cells", 117 "#qcom,smem-state-cells",
118 index, 118 index,
119 &args); 119 &args);
120 if (ret) { 120 if (ret) {
121 dev_err(dev, "failed to parse qcom,state property\n"); 121 dev_err(dev, "failed to parse qcom,smem-states property\n");
122 return ERR_PTR(ret); 122 return ERR_PTR(ret);
123 } 123 }
124 124
125 if (args.args_count != 1) { 125 if (args.args_count != 1) {
126 dev_err(dev, "invalid #qcom,state-cells\n"); 126 dev_err(dev, "invalid #qcom,smem-state-cells\n");
127 return ERR_PTR(-EINVAL); 127 return ERR_PTR(-EINVAL);
128 } 128 }
129 129
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
index f1eed7f9dd67..f51fb2ea7200 100644
--- a/drivers/soc/qcom/smp2p.c
+++ b/drivers/soc/qcom/smp2p.c
@@ -196,7 +196,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
196 /* Match newly created entries */ 196 /* Match newly created entries */
197 for (i = smp2p->valid_entries; i < in->valid_entries; i++) { 197 for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
198 list_for_each_entry(entry, &smp2p->inbound, node) { 198 list_for_each_entry(entry, &smp2p->inbound, node) {
199 memcpy_fromio(buf, in->entries[i].name, sizeof(buf)); 199 memcpy(buf, in->entries[i].name, sizeof(buf));
200 if (!strcmp(buf, entry->name)) { 200 if (!strcmp(buf, entry->name)) {
201 entry->value = &in->entries[i].value; 201 entry->value = &in->entries[i].value;
202 break; 202 break;
@@ -343,12 +343,13 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
343 343
344 /* Allocate an entry from the smem item */ 344 /* Allocate an entry from the smem item */
345 strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); 345 strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
346 memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); 346 memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
347 out->valid_entries++;
348 347
349 /* Make the logical entry reference the physical value */ 348 /* Make the logical entry reference the physical value */
350 entry->value = &out->entries[out->valid_entries].value; 349 entry->value = &out->entries[out->valid_entries].value;
351 350
351 out->valid_entries++;
352
352 entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry); 353 entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry);
353 if (IS_ERR(entry->state)) { 354 if (IS_ERR(entry->state)) {
354 dev_err(smp2p->dev, "failed to register qcom_smem_state\n"); 355 dev_err(smp2p->dev, "failed to register qcom_smem_state\n");
diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c
index 6b777af1bc19..d0337b2a71c8 100644
--- a/drivers/soc/qcom/smsm.c
+++ b/drivers/soc/qcom/smsm.c
@@ -495,7 +495,7 @@ static int qcom_smsm_probe(struct platform_device *pdev)
495 if (!smsm->hosts) 495 if (!smsm->hosts)
496 return -ENOMEM; 496 return -ENOMEM;
497 497
498 local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,state-cells"); 498 local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells");
499 if (!local_node) { 499 if (!local_node) {
500 dev_err(&pdev->dev, "no state entry\n"); 500 dev_err(&pdev->dev, "no state entry\n");
501 return -EINVAL; 501 return -EINVAL;
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index c544f3d2c6ee..520aedd29965 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -1,4 +1,5 @@
1/* 1/*
2 * Copyright (c) 2016, Linaro Ltd.
2 * Copyright (c) 2015, Sony Mobile Communications Inc. 3 * Copyright (c) 2015, Sony Mobile Communications Inc.
3 * 4 *
4 * This program is free software; you can redistribute it and/or modify 5 * This program is free software; you can redistribute it and/or modify
@@ -14,8 +15,16 @@
14#include <linux/module.h> 15#include <linux/module.h>
15#include <linux/slab.h> 16#include <linux/slab.h>
16#include <linux/soc/qcom/smd.h> 17#include <linux/soc/qcom/smd.h>
18#include <linux/io.h>
19#include <linux/of_platform.h>
20#include <linux/platform_device.h>
21#include <linux/soc/qcom/wcnss_ctrl.h>
17 22
18#define WCNSS_REQUEST_TIMEOUT (5 * HZ) 23#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
24#define WCNSS_CBC_TIMEOUT (10 * HZ)
25
26#define WCNSS_ACK_DONE_BOOTING 1
27#define WCNSS_ACK_COLD_BOOTING 2
19 28
20#define NV_FRAGMENT_SIZE 3072 29#define NV_FRAGMENT_SIZE 3072
21#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" 30#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
@@ -25,17 +34,19 @@
25 * @dev: device handle 34 * @dev: device handle
26 * @channel: SMD channel handle 35 * @channel: SMD channel handle
27 * @ack: completion for outstanding requests 36 * @ack: completion for outstanding requests
37 * @cbc: completion for cbc complete indication
28 * @ack_status: status of the outstanding request 38 * @ack_status: status of the outstanding request
29 * @download_nv_work: worker for uploading nv binary 39 * @probe_work: worker for uploading nv binary
30 */ 40 */
31struct wcnss_ctrl { 41struct wcnss_ctrl {
32 struct device *dev; 42 struct device *dev;
33 struct qcom_smd_channel *channel; 43 struct qcom_smd_channel *channel;
34 44
35 struct completion ack; 45 struct completion ack;
46 struct completion cbc;
36 int ack_status; 47 int ack_status;
37 48
38 struct work_struct download_nv_work; 49 struct work_struct probe_work;
39}; 50};
40 51
41/* message types */ 52/* message types */
@@ -48,6 +59,11 @@ enum {
48 WCNSS_UPLOAD_CAL_RESP, 59 WCNSS_UPLOAD_CAL_RESP,
49 WCNSS_DOWNLOAD_CAL_REQ, 60 WCNSS_DOWNLOAD_CAL_REQ,
50 WCNSS_DOWNLOAD_CAL_RESP, 61 WCNSS_DOWNLOAD_CAL_RESP,
62 WCNSS_VBAT_LEVEL_IND,
63 WCNSS_BUILD_VERSION_REQ,
64 WCNSS_BUILD_VERSION_RESP,
65 WCNSS_PM_CONFIG_REQ,
66 WCNSS_CBC_COMPLETE_IND,
51}; 67};
52 68
53/** 69/**
@@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
128 version->major, version->minor, 144 version->major, version->minor,
129 version->version, version->revision); 145 version->version, version->revision);
130 146
131 schedule_work(&wcnss->download_nv_work); 147 complete(&wcnss->ack);
132 break; 148 break;
133 case WCNSS_DOWNLOAD_NV_RESP: 149 case WCNSS_DOWNLOAD_NV_RESP:
134 if (count != sizeof(*nvresp)) { 150 if (count != sizeof(*nvresp)) {
@@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
141 wcnss->ack_status = nvresp->status; 157 wcnss->ack_status = nvresp->status;
142 complete(&wcnss->ack); 158 complete(&wcnss->ack);
143 break; 159 break;
160 case WCNSS_CBC_COMPLETE_IND:
161 dev_dbg(wcnss->dev, "cold boot complete\n");
162 complete(&wcnss->cbc);
163 break;
144 default: 164 default:
145 dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); 165 dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
146 break; 166 break;
@@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
156static int wcnss_request_version(struct wcnss_ctrl *wcnss) 176static int wcnss_request_version(struct wcnss_ctrl *wcnss)
157{ 177{
158 struct wcnss_msg_hdr msg; 178 struct wcnss_msg_hdr msg;
179 int ret;
159 180
160 msg.type = WCNSS_VERSION_REQ; 181 msg.type = WCNSS_VERSION_REQ;
161 msg.len = sizeof(msg); 182 msg.len = sizeof(msg);
183 ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
184 if (ret < 0)
185 return ret;
186
187 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
188 if (!ret) {
189 dev_err(wcnss->dev, "timeout waiting for version response\n");
190 return -ETIMEDOUT;
191 }
162 192
163 return qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); 193 return 0;
164} 194}
165 195
166/** 196/**
167 * wcnss_download_nv() - send nv binary to WCNSS 197 * wcnss_download_nv() - send nv binary to WCNSS
168 * @work: work struct to acquire wcnss context 198 * @wcnss: wcnss_ctrl state handle
199 * @expect_cbc: indicator to caller that an cbc event is expected
200 *
201 * Returns 0 on success. Negative errno on failure.
169 */ 202 */
170static void wcnss_download_nv(struct work_struct *work) 203static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
171{ 204{
172 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
173 struct wcnss_download_nv_req *req; 205 struct wcnss_download_nv_req *req;
174 const struct firmware *fw; 206 const struct firmware *fw;
175 const void *data; 207 const void *data;
@@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work)
178 210
179 req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); 211 req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
180 if (!req) 212 if (!req)
181 return; 213 return -ENOMEM;
182 214
183 ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); 215 ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
184 if (ret) { 216 if (ret < 0) {
185 dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", 217 dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
186 NVBIN_FILE, ret); 218 NVBIN_FILE, ret);
187 goto free_req; 219 goto free_req;
@@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work)
207 memcpy(req->fragment, data, req->frag_size); 239 memcpy(req->fragment, data, req->frag_size);
208 240
209 ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); 241 ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
210 if (ret) { 242 if (ret < 0) {
211 dev_err(wcnss->dev, "failed to send smd packet\n"); 243 dev_err(wcnss->dev, "failed to send smd packet\n");
212 goto release_fw; 244 goto release_fw;
213 } 245 }
@@ -220,16 +252,58 @@ static void wcnss_download_nv(struct work_struct *work)
220 } while (left > 0); 252 } while (left > 0);
221 253
222 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); 254 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
223 if (!ret) 255 if (!ret) {
224 dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); 256 dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
225 else if (wcnss->ack_status != 1) 257 ret = -ETIMEDOUT;
226 dev_err(wcnss->dev, "nv upload response failed err: %d\n", 258 } else {
227 wcnss->ack_status); 259 *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
260 ret = 0;
261 }
228 262
229release_fw: 263release_fw:
230 release_firmware(fw); 264 release_firmware(fw);
231free_req: 265free_req:
232 kfree(req); 266 kfree(req);
267
268 return ret;
269}
270
271/**
272 * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
273 * @wcnss: wcnss handle, retrieved from drvdata
274 * @name: SMD channel name
275 * @cb: callback to handle incoming data on the channel
276 */
277struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb)
278{
279 struct wcnss_ctrl *_wcnss = wcnss;
280
281 return qcom_smd_open_channel(_wcnss->channel, name, cb);
282}
283EXPORT_SYMBOL(qcom_wcnss_open_channel);
284
285static void wcnss_async_probe(struct work_struct *work)
286{
287 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
288 bool expect_cbc;
289 int ret;
290
291 ret = wcnss_request_version(wcnss);
292 if (ret < 0)
293 return;
294
295 ret = wcnss_download_nv(wcnss, &expect_cbc);
296 if (ret < 0)
297 return;
298
299 /* Wait for pending cold boot completion if indicated by the nv downloader */
300 if (expect_cbc) {
301 ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
302 if (!ret)
303 dev_err(wcnss->dev, "expected cold boot completion\n");
304 }
305
306 of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
233} 307}
234 308
235static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) 309static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
@@ -244,25 +318,38 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
244 wcnss->channel = sdev->channel; 318 wcnss->channel = sdev->channel;
245 319
246 init_completion(&wcnss->ack); 320 init_completion(&wcnss->ack);
247 INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv); 321 init_completion(&wcnss->cbc);
322 INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
248 323
249 qcom_smd_set_drvdata(sdev->channel, wcnss); 324 qcom_smd_set_drvdata(sdev->channel, wcnss);
325 dev_set_drvdata(&sdev->dev, wcnss);
326
327 schedule_work(&wcnss->probe_work);
328
329 return 0;
330}
331
332static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
333{
334 struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel);
250 335
251 return wcnss_request_version(wcnss); 336 cancel_work_sync(&wcnss->probe_work);
337 of_platform_depopulate(&sdev->dev);
252} 338}
253 339
254static const struct qcom_smd_id wcnss_ctrl_smd_match[] = { 340static const struct of_device_id wcnss_ctrl_of_match[] = {
255 { .name = "WCNSS_CTRL" }, 341 { .compatible = "qcom,wcnss", },
256 {} 342 {}
257}; 343};
258 344
259static struct qcom_smd_driver wcnss_ctrl_driver = { 345static struct qcom_smd_driver wcnss_ctrl_driver = {
260 .probe = wcnss_ctrl_probe, 346 .probe = wcnss_ctrl_probe,
347 .remove = wcnss_ctrl_remove,
261 .callback = wcnss_ctrl_smd_callback, 348 .callback = wcnss_ctrl_smd_callback,
262 .smd_match_table = wcnss_ctrl_smd_match,
263 .driver = { 349 .driver = {
264 .name = "qcom_wcnss_ctrl", 350 .name = "qcom_wcnss_ctrl",
265 .owner = THIS_MODULE, 351 .owner = THIS_MODULE,
352 .of_match_table = wcnss_ctrl_of_match,
266 }, 353 },
267}; 354};
268 355
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index 9e12000914b3..cc32ab852fbc 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -29,6 +29,14 @@ extern bool qcom_scm_hdcp_available(void);
29extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, 29extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
30 u32 *resp); 30 u32 *resp);
31 31
32extern bool qcom_scm_pas_supported(u32 peripheral);
33extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata,
34 size_t size);
35extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr,
36 phys_addr_t size);
37extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
38extern int qcom_scm_pas_shutdown(u32 peripheral);
39
32#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0 40#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0
33#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1 41#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1
34 42
diff --git a/include/linux/soc/qcom/wcnss_ctrl.h b/include/linux/soc/qcom/wcnss_ctrl.h
new file mode 100644
index 000000000000..a37bc5538f19
--- /dev/null
+++ b/include/linux/soc/qcom/wcnss_ctrl.h
@@ -0,0 +1,8 @@
1#ifndef __WCNSS_CTRL_H__
2#define __WCNSS_CTRL_H__
3
4#include <linux/soc/qcom/smd.h>
5
6struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb);
7
8#endif