aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-qcom
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2014-08-04 21:31:43 -0400
committerKumar Gala <galak@codeaurora.org>2015-01-19 12:53:31 -0500
commitf76c6916570d8d24a622f2107ef747531148046e (patch)
tree9223f5327c2f9f53c0aedf5981eb3f67b2228ba0 /arch/arm/mach-qcom
parente5ca0feb860baacd635aa684c72821e640a1dfa6 (diff)
ARM: qcom: scm: Fix incorrect cache invalidation
The cache invalidation in scm_call() correctly rounds down the start address to invalidate the beginning of the cacheline but doesn't properly round up the 'end' address to make it aligned. The last chunk of the buffer won't be invalidated when 'end' is not cacheline size aligned so make sure to invalidate the last few bytes in such situations. It also doesn't do anything about outer caches so make sure to invalidate and flush those as well. Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Kumar Gala <galak@codeaurora.org>
Diffstat (limited to 'arch/arm/mach-qcom')
-rw-r--r--arch/arm/mach-qcom/scm.c31
1 files changed, 23 insertions, 8 deletions
diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c
index c536fd6bf827..820c72165e19 100644
--- a/arch/arm/mach-qcom/scm.c
+++ b/arch/arm/mach-qcom/scm.c
@@ -22,6 +22,7 @@
22#include <linux/errno.h> 22#include <linux/errno.h>
23#include <linux/err.h> 23#include <linux/err.h>
24 24
25#include <asm/outercache.h>
25#include <asm/cacheflush.h> 26#include <asm/cacheflush.h>
26 27
27#include "scm.h" 28#include "scm.h"
@@ -203,6 +204,7 @@ static int __scm_call(const struct scm_command *cmd)
203 * side in the buffer. 204 * side in the buffer.
204 */ 205 */
205 flush_cache_all(); 206 flush_cache_all();
207 outer_flush_all();
206 ret = smc(cmd_addr); 208 ret = smc(cmd_addr);
207 if (ret < 0) 209 if (ret < 0)
208 ret = scm_remap_error(ret); 210 ret = scm_remap_error(ret);
@@ -210,6 +212,20 @@ static int __scm_call(const struct scm_command *cmd)
210 return ret; 212 return ret;
211} 213}
212 214
215static void scm_inv_range(unsigned long start, unsigned long end)
216{
217 start = round_down(start, CACHELINESIZE);
218 end = round_up(end, CACHELINESIZE);
219 outer_inv_range(start, end);
220 while (start < end) {
221 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
222 : "memory");
223 start += CACHELINESIZE;
224 }
225 dsb();
226 isb();
227}
228
213/** 229/**
214 * scm_call() - Send an SCM command 230 * scm_call() - Send an SCM command
215 * @svc_id: service identifier 231 * @svc_id: service identifier
@@ -227,6 +243,7 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
227 int ret; 243 int ret;
228 struct scm_command *cmd; 244 struct scm_command *cmd;
229 struct scm_response *rsp; 245 struct scm_response *rsp;
246 unsigned long start, end;
230 247
231 cmd = alloc_scm_command(cmd_len, resp_len); 248 cmd = alloc_scm_command(cmd_len, resp_len);
232 if (!cmd) 249 if (!cmd)
@@ -243,17 +260,15 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
243 goto out; 260 goto out;
244 261
245 rsp = scm_command_to_response(cmd); 262 rsp = scm_command_to_response(cmd);
263 start = (unsigned long)rsp;
264
246 do { 265 do {
247 u32 start = (u32)rsp; 266 scm_inv_range(start, start + sizeof(*rsp));
248 u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
249 start &= ~(CACHELINESIZE - 1);
250 while (start < end) {
251 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
252 : "memory");
253 start += CACHELINESIZE;
254 }
255 } while (!rsp->is_complete); 267 } while (!rsp->is_complete);
256 268
269 end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;
270 scm_inv_range(start, end);
271
257 if (resp_buf) 272 if (resp_buf)
258 memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); 273 memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
259out: 274out: