aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2015-04-01 20:35:34 -0400
committerOlof Johansson <olof@lixom.net>2015-04-01 20:35:34 -0400
commit4580cb8a9812c3acc7a1663064d2a2800b3541bd (patch)
treee7411880758cba9e86ac8e1af7dec26f0e935ebe
parent3f9b66b300d14dc2cbcb27e3392bb07a5fe28687 (diff)
parente5fdad68d47ed344832b7ca4e18b2e9708d8141e (diff)
Merge tag 'qcom-soc-for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom into next/drivers
Merge "qcom SoC changes for v4.1" from Kumar Gala: Qualcomm ARM Based SoC Updates for v4.1 * Merged the based Qualcomm SCM and SCM boot support * Cleaned up SCM interface to only expose functional SCM APIs * Moved Qualcomm SCM code into drivers/firmware * Updated the SCM APIs for setting cpu cold and warm boot addresses * Added support for ADM CRCI muxing * tag 'qcom-soc-for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom: soc: qcom: gsbi: Add support for ADM CRCI muxing firmware: qcom: scm: Support cpu power down through SCM firmware: qcom: scm: Add qcom_scm_set_warm_boot_addr function firmware: qcom: scm: Clean cold boot entry to export only the API firmware: qcom: scm: Move the scm driver to drivers/firmware ARM: qcom: Prep scm code for move to drivers/firmware ARM: qcom: Cleanup scm interface to only export what is needed ARM: qcom: Merge scm and scm boot code together Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt30
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/mach-qcom/Kconfig3
-rw-r--r--arch/arm/mach-qcom/Makefile3
-rw-r--r--arch/arm/mach-qcom/platsmp.c23
-rw-r--r--arch/arm/mach-qcom/scm-boot.c39
-rw-r--r--arch/arm/mach-qcom/scm-boot.h26
-rw-r--r--arch/arm/mach-qcom/scm.c326
-rw-r--r--arch/arm/mach-qcom/scm.h25
-rw-r--r--drivers/firmware/Kconfig4
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/qcom_scm.c494
-rw-r--r--drivers/soc/qcom/Kconfig1
-rw-r--r--drivers/soc/qcom/qcom_gsbi.c152
-rw-r--r--include/linux/qcom_scm.h28
16 files changed, 708 insertions, 451 deletions
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
index 4ce24d425bf1..2f5ede39bea2 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
@@ -6,7 +6,8 @@ configuration settings. The mode setting will govern the input/output mode of
6the 4 GSBI IOs. 6the 4 GSBI IOs.
7 7
8Required properties: 8Required properties:
9- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064 9- compatible: Should contain "qcom,gsbi-v1.0.0"
10- cell-index: Should contain the GSBI index
10- reg: Address range for GSBI registers 11- reg: Address range for GSBI registers
11- clocks: required clock 12- clocks: required clock
12- clock-names: must contain "iface" entry 13- clock-names: must contain "iface" entry
@@ -16,6 +17,8 @@ Required properties:
16Optional properties: 17Optional properties:
17- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference 18- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference
18 dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. 19 dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
20- syscon-tcsr: indicates phandle of TCSR syscon node. Required if child uses
21 dma.
19 22
20Required properties if child node exists: 23Required properties if child node exists:
21- #address-cells: Must be 1 24- #address-cells: Must be 1
@@ -39,6 +42,7 @@ Example for APQ8064:
39 42
40 gsbi4@16300000 { 43 gsbi4@16300000 {
41 compatible = "qcom,gsbi-v1.0.0"; 44 compatible = "qcom,gsbi-v1.0.0";
45 cell-index = <4>;
42 reg = <0x16300000 0x100>; 46 reg = <0x16300000 0x100>;
43 clocks = <&gcc GSBI4_H_CLK>; 47 clocks = <&gcc GSBI4_H_CLK>;
44 clock-names = "iface"; 48 clock-names = "iface";
@@ -48,22 +52,24 @@ Example for APQ8064:
48 qcom,mode = <GSBI_PROT_I2C_UART>; 52 qcom,mode = <GSBI_PROT_I2C_UART>;
49 qcom,crci = <GSBI_CRCI_QUP>; 53 qcom,crci = <GSBI_CRCI_QUP>;
50 54
55 syscon-tcsr = <&tcsr>;
56
51 /* child nodes go under here */ 57 /* child nodes go under here */
52 58
53 i2c_qup4: i2c@16380000 { 59 i2c_qup4: i2c@16380000 {
54 compatible = "qcom,i2c-qup-v1.1.1"; 60 compatible = "qcom,i2c-qup-v1.1.1";
55 reg = <0x16380000 0x1000>; 61 reg = <0x16380000 0x1000>;
56 interrupts = <0 153 0>; 62 interrupts = <0 153 0>;
57 63
58 clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; 64 clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>;
59 clock-names = "core", "iface"; 65 clock-names = "core", "iface";
60 66
61 clock-frequency = <200000>; 67 clock-frequency = <200000>;
62 68
63 #address-cells = <1>; 69 #address-cells = <1>;
64 #size-cells = <0>; 70 #size-cells = <0>;
65 71
66 }; 72 };
67 73
68 uart4: serial@16340000 { 74 uart4: serial@16340000 {
69 compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; 75 compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
@@ -76,3 +82,7 @@ Example for APQ8064:
76 }; 82 };
77 }; 83 };
78 84
85 tcsr: syscon@1a400000 {
86 compatible = "qcom,apq8064-tcsr", "syscon";
87 reg = <0x1a400000 0x100>;
88 };
diff --git a/MAINTAINERS b/MAINTAINERS
index eaf999638a65..ad18cd135a87 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1317,6 +1317,7 @@ L: linux-soc@vger.kernel.org
1317S: Maintained 1317S: Maintained
1318F: arch/arm/mach-qcom/ 1318F: arch/arm/mach-qcom/
1319F: drivers/soc/qcom/ 1319F: drivers/soc/qcom/
1320F: drivers/firmware/qcom_scm.c
1320T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git 1321T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git
1321 1322
1322ARM/RADISYS ENP2611 MACHINE SUPPORT 1323ARM/RADISYS ENP2611 MACHINE SUPPORT
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9f1f09a2bc9b..7ffd1518d2aa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2160,6 +2160,8 @@ source "net/Kconfig"
2160 2160
2161source "drivers/Kconfig" 2161source "drivers/Kconfig"
2162 2162
2163source "drivers/firmware/Kconfig"
2164
2163source "fs/Kconfig" 2165source "fs/Kconfig"
2164 2166
2165source "arch/arm/Kconfig.debug" 2167source "arch/arm/Kconfig.debug"
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 48003ea652b9..2256cd1e25d1 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -22,7 +22,4 @@ config ARCH_MSM8974
22 bool "Enable support for MSM8974" 22 bool "Enable support for MSM8974"
23 select HAVE_ARM_ARCH_TIMER 23 select HAVE_ARM_ARCH_TIMER
24 24
25config QCOM_SCM
26 bool
27
28endif 25endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index 8f756ae1ae31..e324375fa919 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,5 +1,2 @@
1obj-y := board.o 1obj-y := board.o
2obj-$(CONFIG_SMP) += platsmp.o 2obj-$(CONFIG_SMP) += platsmp.o
3obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
4
5CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
index 09cffed4c0a4..5cde63a64b34 100644
--- a/arch/arm/mach-qcom/platsmp.c
+++ b/arch/arm/mach-qcom/platsmp.c
@@ -17,10 +17,10 @@
17#include <linux/of_address.h> 17#include <linux/of_address.h>
18#include <linux/smp.h> 18#include <linux/smp.h>
19#include <linux/io.h> 19#include <linux/io.h>
20#include <linux/qcom_scm.h>
20 21
21#include <asm/smp_plat.h> 22#include <asm/smp_plat.h>
22 23
23#include "scm-boot.h"
24 24
25#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 25#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0
26#define SCSS_CPU1CORE_RESET 0x2d80 26#define SCSS_CPU1CORE_RESET 0x2d80
@@ -319,25 +319,10 @@ static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
319 319
320static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) 320static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
321{ 321{
322 int cpu, map; 322 int cpu;
323 unsigned int flags = 0;
324 static const int cold_boot_flags[] = {
325 0,
326 SCM_FLAG_COLDBOOT_CPU1,
327 SCM_FLAG_COLDBOOT_CPU2,
328 SCM_FLAG_COLDBOOT_CPU3,
329 };
330
331 for_each_present_cpu(cpu) {
332 map = cpu_logical_map(cpu);
333 if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) {
334 set_cpu_present(cpu, false);
335 continue;
336 }
337 flags |= cold_boot_flags[map];
338 }
339 323
340 if (scm_set_boot_addr(virt_to_phys(secondary_startup_arm), flags)) { 324 if (qcom_scm_set_cold_boot_addr(secondary_startup_arm,
325 cpu_present_mask)) {
341 for_each_present_cpu(cpu) { 326 for_each_present_cpu(cpu) {
342 if (cpu == smp_processor_id()) 327 if (cpu == smp_processor_id())
343 continue; 328 continue;
diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c
deleted file mode 100644
index e8ff7beb6218..000000000000
--- a/arch/arm/mach-qcom/scm-boot.c
+++ /dev/null
@@ -1,39 +0,0 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#include <linux/module.h>
19#include <linux/slab.h>
20
21#include "scm.h"
22#include "scm-boot.h"
23
24/*
25 * Set the cold/warm boot address for one of the CPU cores.
26 */
27int scm_set_boot_addr(u32 addr, int flags)
28{
29 struct {
30 __le32 flags;
31 __le32 addr;
32 } cmd;
33
34 cmd.addr = cpu_to_le32(addr);
35 cmd.flags = cpu_to_le32(flags);
36 return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
37 &cmd, sizeof(cmd), NULL, 0);
38}
39EXPORT_SYMBOL(scm_set_boot_addr);
diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h
deleted file mode 100644
index 3e210fb818bb..000000000000
--- a/arch/arm/mach-qcom/scm-boot.h
+++ /dev/null
@@ -1,26 +0,0 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#ifndef __MACH_SCM_BOOT_H
13#define __MACH_SCM_BOOT_H
14
15#define SCM_BOOT_ADDR 0x1
16#define SCM_FLAG_COLDBOOT_CPU1 0x01
17#define SCM_FLAG_COLDBOOT_CPU2 0x08
18#define SCM_FLAG_COLDBOOT_CPU3 0x20
19#define SCM_FLAG_WARMBOOT_CPU0 0x04
20#define SCM_FLAG_WARMBOOT_CPU1 0x02
21#define SCM_FLAG_WARMBOOT_CPU2 0x10
22#define SCM_FLAG_WARMBOOT_CPU3 0x40
23
24int scm_set_boot_addr(u32 addr, int flags);
25
26#endif
diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c
deleted file mode 100644
index 1d9cf18c7091..000000000000
--- a/arch/arm/mach-qcom/scm.c
+++ /dev/null
@@ -1,326 +0,0 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#include <linux/slab.h>
19#include <linux/io.h>
20#include <linux/module.h>
21#include <linux/mutex.h>
22#include <linux/errno.h>
23#include <linux/err.h>
24
25#include <asm/outercache.h>
26#include <asm/cacheflush.h>
27
28#include "scm.h"
29
30#define SCM_ENOMEM -5
31#define SCM_EOPNOTSUPP -4
32#define SCM_EINVAL_ADDR -3
33#define SCM_EINVAL_ARG -2
34#define SCM_ERROR -1
35#define SCM_INTERRUPTED 1
36
37static DEFINE_MUTEX(scm_lock);
38
39/**
40 * struct scm_command - one SCM command buffer
41 * @len: total available memory for command and response
42 * @buf_offset: start of command buffer
43 * @resp_hdr_offset: start of response buffer
44 * @id: command to be executed
45 * @buf: buffer returned from scm_get_command_buffer()
46 *
47 * An SCM command is laid out in memory as follows:
48 *
49 * ------------------- <--- struct scm_command
50 * | command header |
51 * ------------------- <--- scm_get_command_buffer()
52 * | command buffer |
53 * ------------------- <--- struct scm_response and
54 * | response header | scm_command_to_response()
55 * ------------------- <--- scm_get_response_buffer()
56 * | response buffer |
57 * -------------------
58 *
59 * There can be arbitrary padding between the headers and buffers so
60 * you should always use the appropriate scm_get_*_buffer() routines
61 * to access the buffers in a safe manner.
62 */
63struct scm_command {
64 __le32 len;
65 __le32 buf_offset;
66 __le32 resp_hdr_offset;
67 __le32 id;
68 __le32 buf[0];
69};
70
71/**
72 * struct scm_response - one SCM response buffer
73 * @len: total available memory for response
74 * @buf_offset: start of response data relative to start of scm_response
75 * @is_complete: indicates if the command has finished processing
76 */
77struct scm_response {
78 __le32 len;
79 __le32 buf_offset;
80 __le32 is_complete;
81};
82
83/**
84 * alloc_scm_command() - Allocate an SCM command
85 * @cmd_size: size of the command buffer
86 * @resp_size: size of the response buffer
87 *
88 * Allocate an SCM command, including enough room for the command
89 * and response headers as well as the command and response buffers.
90 *
91 * Returns a valid &scm_command on success or %NULL if the allocation fails.
92 */
93static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
94{
95 struct scm_command *cmd;
96 size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
97 resp_size;
98 u32 offset;
99
100 cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
101 if (cmd) {
102 cmd->len = cpu_to_le32(len);
103 offset = offsetof(struct scm_command, buf);
104 cmd->buf_offset = cpu_to_le32(offset);
105 cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size);
106 }
107 return cmd;
108}
109
110/**
111 * free_scm_command() - Free an SCM command
112 * @cmd: command to free
113 *
114 * Free an SCM command.
115 */
116static inline void free_scm_command(struct scm_command *cmd)
117{
118 kfree(cmd);
119}
120
121/**
122 * scm_command_to_response() - Get a pointer to a scm_response
123 * @cmd: command
124 *
125 * Returns a pointer to a response for a command.
126 */
127static inline struct scm_response *scm_command_to_response(
128 const struct scm_command *cmd)
129{
130 return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
131}
132
133/**
134 * scm_get_command_buffer() - Get a pointer to a command buffer
135 * @cmd: command
136 *
137 * Returns a pointer to the command buffer of a command.
138 */
139static inline void *scm_get_command_buffer(const struct scm_command *cmd)
140{
141 return (void *)cmd->buf;
142}
143
144/**
145 * scm_get_response_buffer() - Get a pointer to a response buffer
146 * @rsp: response
147 *
148 * Returns a pointer to a response buffer of a response.
149 */
150static inline void *scm_get_response_buffer(const struct scm_response *rsp)
151{
152 return (void *)rsp + le32_to_cpu(rsp->buf_offset);
153}
154
155static int scm_remap_error(int err)
156{
157 pr_err("scm_call failed with error code %d\n", err);
158 switch (err) {
159 case SCM_ERROR:
160 return -EIO;
161 case SCM_EINVAL_ADDR:
162 case SCM_EINVAL_ARG:
163 return -EINVAL;
164 case SCM_EOPNOTSUPP:
165 return -EOPNOTSUPP;
166 case SCM_ENOMEM:
167 return -ENOMEM;
168 }
169 return -EINVAL;
170}
171
172static u32 smc(u32 cmd_addr)
173{
174 int context_id;
175 register u32 r0 asm("r0") = 1;
176 register u32 r1 asm("r1") = (u32)&context_id;
177 register u32 r2 asm("r2") = cmd_addr;
178 do {
179 asm volatile(
180 __asmeq("%0", "r0")
181 __asmeq("%1", "r0")
182 __asmeq("%2", "r1")
183 __asmeq("%3", "r2")
184#ifdef REQUIRES_SEC
185 ".arch_extension sec\n"
186#endif
187 "smc #0 @ switch to secure world\n"
188 : "=r" (r0)
189 : "r" (r0), "r" (r1), "r" (r2)
190 : "r3");
191 } while (r0 == SCM_INTERRUPTED);
192
193 return r0;
194}
195
196static int __scm_call(const struct scm_command *cmd)
197{
198 int ret;
199 u32 cmd_addr = virt_to_phys(cmd);
200
201 /*
202 * Flush the command buffer so that the secure world sees
203 * the correct data.
204 */
205 __cpuc_flush_dcache_area((void *)cmd, cmd->len);
206 outer_flush_range(cmd_addr, cmd_addr + cmd->len);
207
208 ret = smc(cmd_addr);
209 if (ret < 0)
210 ret = scm_remap_error(ret);
211
212 return ret;
213}
214
215static void scm_inv_range(unsigned long start, unsigned long end)
216{
217 u32 cacheline_size, ctr;
218
219 asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
220 cacheline_size = 4 << ((ctr >> 16) & 0xf);
221
222 start = round_down(start, cacheline_size);
223 end = round_up(end, cacheline_size);
224 outer_inv_range(start, end);
225 while (start < end) {
226 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
227 : "memory");
228 start += cacheline_size;
229 }
230 dsb();
231 isb();
232}
233
234/**
235 * scm_call() - Send an SCM command
236 * @svc_id: service identifier
237 * @cmd_id: command identifier
238 * @cmd_buf: command buffer
239 * @cmd_len: length of the command buffer
240 * @resp_buf: response buffer
241 * @resp_len: length of the response buffer
242 *
243 * Sends a command to the SCM and waits for the command to finish processing.
244 *
245 * A note on cache maintenance:
246 * Note that any buffers that are expected to be accessed by the secure world
247 * must be flushed before invoking scm_call and invalidated in the cache
248 * immediately after scm_call returns. Cache maintenance on the command and
249 * response buffers is taken care of by scm_call; however, callers are
250 * responsible for any other cached buffers passed over to the secure world.
251 */
252int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
253 void *resp_buf, size_t resp_len)
254{
255 int ret;
256 struct scm_command *cmd;
257 struct scm_response *rsp;
258 unsigned long start, end;
259
260 cmd = alloc_scm_command(cmd_len, resp_len);
261 if (!cmd)
262 return -ENOMEM;
263
264 cmd->id = cpu_to_le32((svc_id << 10) | cmd_id);
265 if (cmd_buf)
266 memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
267
268 mutex_lock(&scm_lock);
269 ret = __scm_call(cmd);
270 mutex_unlock(&scm_lock);
271 if (ret)
272 goto out;
273
274 rsp = scm_command_to_response(cmd);
275 start = (unsigned long)rsp;
276
277 do {
278 scm_inv_range(start, start + sizeof(*rsp));
279 } while (!rsp->is_complete);
280
281 end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;
282 scm_inv_range(start, end);
283
284 if (resp_buf)
285 memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
286out:
287 free_scm_command(cmd);
288 return ret;
289}
290EXPORT_SYMBOL(scm_call);
291
292u32 scm_get_version(void)
293{
294 int context_id;
295 static u32 version = -1;
296 register u32 r0 asm("r0");
297 register u32 r1 asm("r1");
298
299 if (version != -1)
300 return version;
301
302 mutex_lock(&scm_lock);
303
304 r0 = 0x1 << 8;
305 r1 = (u32)&context_id;
306 do {
307 asm volatile(
308 __asmeq("%0", "r0")
309 __asmeq("%1", "r1")
310 __asmeq("%2", "r0")
311 __asmeq("%3", "r1")
312#ifdef REQUIRES_SEC
313 ".arch_extension sec\n"
314#endif
315 "smc #0 @ switch to secure world\n"
316 : "=r" (r0), "=r" (r1)
317 : "r" (r0), "r" (r1)
318 : "r2", "r3");
319 } while (r0 == SCM_INTERRUPTED);
320
321 version = r1;
322 mutex_unlock(&scm_lock);
323
324 return version;
325}
326EXPORT_SYMBOL(scm_get_version);
diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h
deleted file mode 100644
index 00b31ea58f29..000000000000
--- a/arch/arm/mach-qcom/scm.h
+++ /dev/null
@@ -1,25 +0,0 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#ifndef __MACH_SCM_H
13#define __MACH_SCM_H
14
15#define SCM_SVC_BOOT 0x1
16#define SCM_SVC_PIL 0x2
17
18extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
19 void *resp_buf, size_t resp_len);
20
21#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
22
23extern u32 scm_get_version(void);
24
25#endif
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 41983883cef4..6517132e5d8b 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -132,6 +132,10 @@ config ISCSI_IBFT
132 detect iSCSI boot parameters dynamically during system boot, say Y. 132 detect iSCSI boot parameters dynamically during system boot, say Y.
133 Otherwise, say N. 133 Otherwise, say N.
134 134
135config QCOM_SCM
136 bool
137 depends on ARM || ARM64
138
135source "drivers/firmware/google/Kconfig" 139source "drivers/firmware/google/Kconfig"
136source "drivers/firmware/efi/Kconfig" 140source "drivers/firmware/efi/Kconfig"
137 141
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 5373dc5b6011..3fdd3912709a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_DMIID) += dmi-id.o
11obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o 11obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
12obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o 12obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
13obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o 13obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
14obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
15CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
14 16
15obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ 17obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
16obj-$(CONFIG_EFI) += efi/ 18obj-$(CONFIG_EFI) += efi/
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
new file mode 100644
index 000000000000..994b50fd997c
--- /dev/null
+++ b/drivers/firmware/qcom_scm.c
@@ -0,0 +1,494 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 * Copyright (C) 2015 Linaro Ltd.
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 *
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 */
18
19#include <linux/slab.h>
20#include <linux/io.h>
21#include <linux/module.h>
22#include <linux/mutex.h>
23#include <linux/errno.h>
24#include <linux/err.h>
25#include <linux/qcom_scm.h>
26
27#include <asm/outercache.h>
28#include <asm/cacheflush.h>
29
30
31#define QCOM_SCM_ENOMEM -5
32#define QCOM_SCM_EOPNOTSUPP -4
33#define QCOM_SCM_EINVAL_ADDR -3
34#define QCOM_SCM_EINVAL_ARG -2
35#define QCOM_SCM_ERROR -1
36#define QCOM_SCM_INTERRUPTED 1
37
38#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00
39#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01
40#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08
41#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20
42
43#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04
44#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02
45#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10
46#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40
47
48struct qcom_scm_entry {
49 int flag;
50 void *entry;
51};
52
53static struct qcom_scm_entry qcom_scm_wb[] = {
54 { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 },
55 { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 },
56 { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 },
57 { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
58};
59
60static DEFINE_MUTEX(qcom_scm_lock);
61
62/**
63 * struct qcom_scm_command - one SCM command buffer
64 * @len: total available memory for command and response
65 * @buf_offset: start of command buffer
66 * @resp_hdr_offset: start of response buffer
67 * @id: command to be executed
68 * @buf: buffer returned from qcom_scm_get_command_buffer()
69 *
70 * An SCM command is laid out in memory as follows:
71 *
72 * ------------------- <--- struct qcom_scm_command
73 * | command header |
74 * ------------------- <--- qcom_scm_get_command_buffer()
75 * | command buffer |
76 * ------------------- <--- struct qcom_scm_response and
77 * | response header | qcom_scm_command_to_response()
78 * ------------------- <--- qcom_scm_get_response_buffer()
79 * | response buffer |
80 * -------------------
81 *
82 * There can be arbitrary padding between the headers and buffers so
83 * you should always use the appropriate qcom_scm_get_*_buffer() routines
84 * to access the buffers in a safe manner.
85 */
86struct qcom_scm_command {
87 __le32 len;
88 __le32 buf_offset;
89 __le32 resp_hdr_offset;
90 __le32 id;
91 __le32 buf[0];
92};
93
94/**
95 * struct qcom_scm_response - one SCM response buffer
96 * @len: total available memory for response
97 * @buf_offset: start of response data relative to start of qcom_scm_response
98 * @is_complete: indicates if the command has finished processing
99 */
100struct qcom_scm_response {
101 __le32 len;
102 __le32 buf_offset;
103 __le32 is_complete;
104};
105
106/**
107 * alloc_qcom_scm_command() - Allocate an SCM command
108 * @cmd_size: size of the command buffer
109 * @resp_size: size of the response buffer
110 *
111 * Allocate an SCM command, including enough room for the command
112 * and response headers as well as the command and response buffers.
113 *
114 * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails.
115 */
116static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size)
117{
118 struct qcom_scm_command *cmd;
119 size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size +
120 resp_size;
121 u32 offset;
122
123 cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
124 if (cmd) {
125 cmd->len = cpu_to_le32(len);
126 offset = offsetof(struct qcom_scm_command, buf);
127 cmd->buf_offset = cpu_to_le32(offset);
128 cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size);
129 }
130 return cmd;
131}
132
133/**
134 * free_qcom_scm_command() - Free an SCM command
135 * @cmd: command to free
136 *
137 * Free an SCM command.
138 */
139static inline void free_qcom_scm_command(struct qcom_scm_command *cmd)
140{
141 kfree(cmd);
142}
143
144/**
145 * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response
146 * @cmd: command
147 *
148 * Returns a pointer to a response for a command.
149 */
150static inline struct qcom_scm_response *qcom_scm_command_to_response(
151 const struct qcom_scm_command *cmd)
152{
153 return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
154}
155
156/**
157 * qcom_scm_get_command_buffer() - Get a pointer to a command buffer
158 * @cmd: command
159 *
160 * Returns a pointer to the command buffer of a command.
161 */
162static inline void *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd)
163{
164 return (void *)cmd->buf;
165}
166
167/**
168 * qcom_scm_get_response_buffer() - Get a pointer to a response buffer
169 * @rsp: response
170 *
171 * Returns a pointer to a response buffer of a response.
172 */
173static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp)
174{
175 return (void *)rsp + le32_to_cpu(rsp->buf_offset);
176}
177
178static int qcom_scm_remap_error(int err)
179{
180 pr_err("qcom_scm_call failed with error code %d\n", err);
181 switch (err) {
182 case QCOM_SCM_ERROR:
183 return -EIO;
184 case QCOM_SCM_EINVAL_ADDR:
185 case QCOM_SCM_EINVAL_ARG:
186 return -EINVAL;
187 case QCOM_SCM_EOPNOTSUPP:
188 return -EOPNOTSUPP;
189 case QCOM_SCM_ENOMEM:
190 return -ENOMEM;
191 }
192 return -EINVAL;
193}
194
195static u32 smc(u32 cmd_addr)
196{
197 int context_id;
198 register u32 r0 asm("r0") = 1;
199 register u32 r1 asm("r1") = (u32)&context_id;
200 register u32 r2 asm("r2") = cmd_addr;
201 do {
202 asm volatile(
203 __asmeq("%0", "r0")
204 __asmeq("%1", "r0")
205 __asmeq("%2", "r1")
206 __asmeq("%3", "r2")
207#ifdef REQUIRES_SEC
208 ".arch_extension sec\n"
209#endif
210 "smc #0 @ switch to secure world\n"
211 : "=r" (r0)
212 : "r" (r0), "r" (r1), "r" (r2)
213 : "r3");
214 } while (r0 == QCOM_SCM_INTERRUPTED);
215
216 return r0;
217}
218
219static int __qcom_scm_call(const struct qcom_scm_command *cmd)
220{
221 int ret;
222 u32 cmd_addr = virt_to_phys(cmd);
223
224 /*
225 * Flush the command buffer so that the secure world sees
226 * the correct data.
227 */
228 __cpuc_flush_dcache_area((void *)cmd, cmd->len);
229 outer_flush_range(cmd_addr, cmd_addr + cmd->len);
230
231 ret = smc(cmd_addr);
232 if (ret < 0)
233 ret = qcom_scm_remap_error(ret);
234
235 return ret;
236}
237
238static void qcom_scm_inv_range(unsigned long start, unsigned long end)
239{
240 u32 cacheline_size, ctr;
241
242 asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
243 cacheline_size = 4 << ((ctr >> 16) & 0xf);
244
245 start = round_down(start, cacheline_size);
246 end = round_up(end, cacheline_size);
247 outer_inv_range(start, end);
248 while (start < end) {
249 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
250 : "memory");
251 start += cacheline_size;
252 }
253 dsb();
254 isb();
255}
256
257/**
258 * qcom_scm_call() - Send an SCM command
259 * @svc_id: service identifier
260 * @cmd_id: command identifier
261 * @cmd_buf: command buffer
262 * @cmd_len: length of the command buffer
263 * @resp_buf: response buffer
264 * @resp_len: length of the response buffer
265 *
266 * Sends a command to the SCM and waits for the command to finish processing.
267 *
268 * A note on cache maintenance:
269 * Note that any buffers that are expected to be accessed by the secure world
270 * must be flushed before invoking qcom_scm_call and invalidated in the cache
271 * immediately after qcom_scm_call returns. Cache maintenance on the command
272 * and response buffers is taken care of by qcom_scm_call; however, callers are
273 * responsible for any other cached buffers passed over to the secure world.
274 */
275static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf,
276 size_t cmd_len, void *resp_buf, size_t resp_len)
277{
278 int ret;
279 struct qcom_scm_command *cmd;
280 struct qcom_scm_response *rsp;
281 unsigned long start, end;
282
283 cmd = alloc_qcom_scm_command(cmd_len, resp_len);
284 if (!cmd)
285 return -ENOMEM;
286
287 cmd->id = cpu_to_le32((svc_id << 10) | cmd_id);
288 if (cmd_buf)
289 memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len);
290
291 mutex_lock(&qcom_scm_lock);
292 ret = __qcom_scm_call(cmd);
293 mutex_unlock(&qcom_scm_lock);
294 if (ret)
295 goto out;
296
297 rsp = qcom_scm_command_to_response(cmd);
298 start = (unsigned long)rsp;
299
300 do {
301 qcom_scm_inv_range(start, start + sizeof(*rsp));
302 } while (!rsp->is_complete);
303
304 end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len;
305 qcom_scm_inv_range(start, end);
306
307 if (resp_buf)
308 memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len);
309out:
310 free_qcom_scm_command(cmd);
311 return ret;
312}
313
314#define SCM_CLASS_REGISTER (0x2 << 8)
315#define SCM_MASK_IRQS BIT(5)
316#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \
317 SCM_CLASS_REGISTER | \
318 SCM_MASK_IRQS | \
319 (n & 0xf))
320
321/**
322 * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument
323 * @svc_id: service identifier
324 * @cmd_id: command identifier
325 * @arg1: first argument
326 *
327 * This shall only be used with commands that are guaranteed to be
328 * uninterruptable, atomic and SMP safe.
329 */
330static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
331{
332 int context_id;
333
334 register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1);
335 register u32 r1 asm("r1") = (u32)&context_id;
336 register u32 r2 asm("r2") = arg1;
337
338 asm volatile(
339 __asmeq("%0", "r0")
340 __asmeq("%1", "r0")
341 __asmeq("%2", "r1")
342 __asmeq("%3", "r2")
343#ifdef REQUIRES_SEC
344 ".arch_extension sec\n"
345#endif
346 "smc #0 @ switch to secure world\n"
347 : "=r" (r0)
348 : "r" (r0), "r" (r1), "r" (r2)
349 : "r3");
350 return r0;
351}
352
353u32 qcom_scm_get_version(void)
354{
355 int context_id;
356 static u32 version = -1;
357 register u32 r0 asm("r0");
358 register u32 r1 asm("r1");
359
360 if (version != -1)
361 return version;
362
363 mutex_lock(&qcom_scm_lock);
364
365 r0 = 0x1 << 8;
366 r1 = (u32)&context_id;
367 do {
368 asm volatile(
369 __asmeq("%0", "r0")
370 __asmeq("%1", "r1")
371 __asmeq("%2", "r0")
372 __asmeq("%3", "r1")
373#ifdef REQUIRES_SEC
374 ".arch_extension sec\n"
375#endif
376 "smc #0 @ switch to secure world\n"
377 : "=r" (r0), "=r" (r1)
378 : "r" (r0), "r" (r1)
379 : "r2", "r3");
380 } while (r0 == QCOM_SCM_INTERRUPTED);
381
382 version = r1;
383 mutex_unlock(&qcom_scm_lock);
384
385 return version;
386}
387EXPORT_SYMBOL(qcom_scm_get_version);
388
389#define QCOM_SCM_SVC_BOOT 0x1
390#define QCOM_SCM_BOOT_ADDR 0x1
391/*
392 * Set the cold/warm boot address for one of the CPU cores.
393 */
394static int qcom_scm_set_boot_addr(u32 addr, int flags)
395{
396 struct {
397 __le32 flags;
398 __le32 addr;
399 } cmd;
400
401 cmd.addr = cpu_to_le32(addr);
402 cmd.flags = cpu_to_le32(flags);
403 return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
404 &cmd, sizeof(cmd), NULL, 0);
405}
406
407/**
408 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
409 * @entry: Entry point function for the cpus
410 * @cpus: The cpumask of cpus that will use the entry point
411 *
412 * Set the cold boot address of the cpus. Any cpu outside the supported
413 * range would be removed from the cpu present mask.
414 */
415int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
416{
417 int flags = 0;
418 int cpu;
419 int scm_cb_flags[] = {
420 QCOM_SCM_FLAG_COLDBOOT_CPU0,
421 QCOM_SCM_FLAG_COLDBOOT_CPU1,
422 QCOM_SCM_FLAG_COLDBOOT_CPU2,
423 QCOM_SCM_FLAG_COLDBOOT_CPU3,
424 };
425
426 if (!cpus || (cpus && cpumask_empty(cpus)))
427 return -EINVAL;
428
429 for_each_cpu(cpu, cpus) {
430 if (cpu < ARRAY_SIZE(scm_cb_flags))
431 flags |= scm_cb_flags[cpu];
432 else
433 set_cpu_present(cpu, false);
434 }
435
436 return qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
437}
438EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
439
440/**
441 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
442 * @entry: Entry point function for the cpus
443 * @cpus: The cpumask of cpus that will use the entry point
444 *
445 * Set the Linux entry point for the SCM to transfer control to when coming
446 * out of a power down. CPU power down may be executed on cpuidle or hotplug.
447 */
448int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
449{
450 int ret;
451 int flags = 0;
452 int cpu;
453
454 /*
455 * Reassign only if we are switching from hotplug entry point
456 * to cpuidle entry point or vice versa.
457 */
458 for_each_cpu(cpu, cpus) {
459 if (entry == qcom_scm_wb[cpu].entry)
460 continue;
461 flags |= qcom_scm_wb[cpu].flag;
462 }
463
464 /* No change in entry function */
465 if (!flags)
466 return 0;
467
468 ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
469 if (!ret) {
470 for_each_cpu(cpu, cpus)
471 qcom_scm_wb[cpu].entry = entry;
472 }
473
474 return ret;
475}
476EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
477
478#define QCOM_SCM_CMD_TERMINATE_PC 0x2
479#define QCOM_SCM_FLUSH_FLAG_MASK 0x3
480
481/**
482 * qcom_scm_cpu_power_down() - Power down the cpu
483 * @flags - Flags to flush cache
484 *
485 * This is an end point to power down cpu. If there was a pending interrupt,
486 * the control would return from this function, otherwise, the cpu jumps to the
487 * warm boot entry point set for this cpu upon reset.
488 */
489void qcom_scm_cpu_power_down(u32 flags)
490{
491 qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC,
492 flags & QCOM_SCM_FLUSH_FLAG_MASK);
493}
494EXPORT_SYMBOL(qcom_scm_cpu_power_down);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 7bd2c94f54a4..460b2dba109c 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -4,6 +4,7 @@
4config QCOM_GSBI 4config QCOM_GSBI
5 tristate "QCOM General Serial Bus Interface" 5 tristate "QCOM General Serial Bus Interface"
6 depends on ARCH_QCOM 6 depends on ARCH_QCOM
7 select MFD_SYSCON
7 help 8 help
8 Say y here to enable GSBI support. The GSBI provides control 9 Say y here to enable GSBI support. The GSBI provides control
9 functions for connecting the underlying serial UART, SPI, and I2C 10 functions for connecting the underlying serial UART, SPI, and I2C
diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
index 729425ddfd3e..09c669e70d63 100644
--- a/drivers/soc/qcom/qcom_gsbi.c
+++ b/drivers/soc/qcom/qcom_gsbi.c
@@ -18,22 +18,129 @@
18#include <linux/of.h> 18#include <linux/of.h>
19#include <linux/of_platform.h> 19#include <linux/of_platform.h>
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/regmap.h>
22#include <linux/mfd/syscon.h>
23#include <dt-bindings/soc/qcom,gsbi.h>
21 24
22#define GSBI_CTRL_REG 0x0000 25#define GSBI_CTRL_REG 0x0000
23#define GSBI_PROTOCOL_SHIFT 4 26#define GSBI_PROTOCOL_SHIFT 4
27#define MAX_GSBI 12
28
29#define TCSR_ADM_CRCI_BASE 0x70
30
31struct crci_config {
32 u32 num_rows;
33 const u32 (*array)[MAX_GSBI];
34};
35
36static const u32 crci_ipq8064[][MAX_GSBI] = {
37 {
38 0x000003, 0x00000c, 0x000030, 0x0000c0,
39 0x000300, 0x000c00, 0x003000, 0x00c000,
40 0x030000, 0x0c0000, 0x300000, 0xc00000
41 },
42 {
43 0x000003, 0x00000c, 0x000030, 0x0000c0,
44 0x000300, 0x000c00, 0x003000, 0x00c000,
45 0x030000, 0x0c0000, 0x300000, 0xc00000
46 },
47};
48
49static const struct crci_config config_ipq8064 = {
50 .num_rows = ARRAY_SIZE(crci_ipq8064),
51 .array = crci_ipq8064,
52};
53
54static const unsigned int crci_apq8064[][MAX_GSBI] = {
55 {
56 0x001800, 0x006000, 0x000030, 0x0000c0,
57 0x000300, 0x000400, 0x000000, 0x000000,
58 0x000000, 0x000000, 0x000000, 0x000000
59 },
60 {
61 0x000000, 0x000000, 0x000000, 0x000000,
62 0x000000, 0x000020, 0x0000c0, 0x000000,
63 0x000000, 0x000000, 0x000000, 0x000000
64 },
65};
66
67static const struct crci_config config_apq8064 = {
68 .num_rows = ARRAY_SIZE(crci_apq8064),
69 .array = crci_apq8064,
70};
71
72static const unsigned int crci_msm8960[][MAX_GSBI] = {
73 {
74 0x000003, 0x00000c, 0x000030, 0x0000c0,
75 0x000300, 0x000400, 0x000000, 0x000000,
76 0x000000, 0x000000, 0x000000, 0x000000
77 },
78 {
79 0x000000, 0x000000, 0x000000, 0x000000,
80 0x000000, 0x000020, 0x0000c0, 0x000300,
81 0x001800, 0x006000, 0x000000, 0x000000
82 },
83};
84
85static const struct crci_config config_msm8960 = {
86 .num_rows = ARRAY_SIZE(crci_msm8960),
87 .array = crci_msm8960,
88};
89
90static const unsigned int crci_msm8660[][MAX_GSBI] = {
91 { /* ADM 0 - B */
92 0x000003, 0x00000c, 0x000030, 0x0000c0,
93 0x000300, 0x000c00, 0x003000, 0x00c000,
94 0x030000, 0x0c0000, 0x300000, 0xc00000
95 },
96 { /* ADM 0 - B */
97 0x000003, 0x00000c, 0x000030, 0x0000c0,
98 0x000300, 0x000c00, 0x003000, 0x00c000,
99 0x030000, 0x0c0000, 0x300000, 0xc00000
100 },
101 { /* ADM 1 - A */
102 0x000003, 0x00000c, 0x000030, 0x0000c0,
103 0x000300, 0x000c00, 0x003000, 0x00c000,
104 0x030000, 0x0c0000, 0x300000, 0xc00000
105 },
106 { /* ADM 1 - B */
107 0x000003, 0x00000c, 0x000030, 0x0000c0,
108 0x000300, 0x000c00, 0x003000, 0x00c000,
109 0x030000, 0x0c0000, 0x300000, 0xc00000
110 },
111};
112
113static const struct crci_config config_msm8660 = {
114 .num_rows = ARRAY_SIZE(crci_msm8660),
115 .array = crci_msm8660,
116};
24 117
25struct gsbi_info { 118struct gsbi_info {
26 struct clk *hclk; 119 struct clk *hclk;
27 u32 mode; 120 u32 mode;
28 u32 crci; 121 u32 crci;
122 struct regmap *tcsr;
123};
124
125static const struct of_device_id tcsr_dt_match[] = {
126 { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
127 { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
128 { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
129 { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
130 { },
29}; 131};
30 132
31static int gsbi_probe(struct platform_device *pdev) 133static int gsbi_probe(struct platform_device *pdev)
32{ 134{
33 struct device_node *node = pdev->dev.of_node; 135 struct device_node *node = pdev->dev.of_node;
136 struct device_node *tcsr_node;
137 const struct of_device_id *match;
34 struct resource *res; 138 struct resource *res;
35 void __iomem *base; 139 void __iomem *base;
36 struct gsbi_info *gsbi; 140 struct gsbi_info *gsbi;
141 int i;
142 u32 mask, gsbi_num;
143 const struct crci_config *config = NULL;
37 144
38 gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL); 145 gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
39 146
@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_device *pdev)
45 if (IS_ERR(base)) 152 if (IS_ERR(base))
46 return PTR_ERR(base); 153 return PTR_ERR(base);
47 154
155 /* get the tcsr node and setup the config and regmap */
156 gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
157
158 if (!IS_ERR(gsbi->tcsr)) {
159 tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
160 if (tcsr_node) {
161 match = of_match_node(tcsr_dt_match, tcsr_node);
162 if (match)
163 config = match->data;
164 else
165 dev_warn(&pdev->dev, "no matching TCSR\n");
166
167 of_node_put(tcsr_node);
168 }
169 }
170
171 if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
172 dev_err(&pdev->dev, "missing cell-index\n");
173 return -EINVAL;
174 }
175
176 if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
177 dev_err(&pdev->dev, "invalid cell-index\n");
178 return -EINVAL;
179 }
180
48 if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) { 181 if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
49 dev_err(&pdev->dev, "missing mode configuration\n"); 182 dev_err(&pdev->dev, "missing mode configuration\n");
50 return -EINVAL; 183 return -EINVAL;
@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_device *pdev)
64 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci, 197 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
65 base + GSBI_CTRL_REG); 198 base + GSBI_CTRL_REG);
66 199
200 /*
201 * modify tcsr to reflect mode and ADM CRCI mux
202 * Each gsbi contains a pair of bits, one for RX and one for TX
203 * SPI mode requires both bits cleared, otherwise they are set
204 */
205 if (config) {
206 for (i = 0; i < config->num_rows; i++) {
207 mask = config->array[i][gsbi_num - 1];
208
209 if (gsbi->mode == GSBI_PROT_SPI)
210 regmap_update_bits(gsbi->tcsr,
211 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
212 else
213 regmap_update_bits(gsbi->tcsr,
214 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
215
216 }
217 }
218
67 /* make sure the gsbi control write is not reordered */ 219 /* make sure the gsbi control write is not reordered */
68 wmb(); 220 wmb();
69 221
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
new file mode 100644
index 000000000000..d7a974d5f57c
--- /dev/null
+++ b/include/linux/qcom_scm.h
@@ -0,0 +1,28 @@
1/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
2 * Copyright (C) 2015 Linaro Ltd.
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 */
13#ifndef __QCOM_SCM_H
14#define __QCOM_SCM_H
15
16extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
17extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus);
18
19#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0
20#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1
21
22extern void qcom_scm_cpu_power_down(u32 flags);
23
24#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
25
26extern u32 qcom_scm_get_version(void);
27
28#endif