aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/qcom_scm.c57
-rw-r--r--include/linux/qcom_scm.h7
2 files changed, 63 insertions, 1 deletions
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 4d8ede4807ac..994b50fd997c 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -311,6 +311,45 @@ out:
311 return ret; 311 return ret;
312} 312}
313 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
314u32 qcom_scm_get_version(void) 353u32 qcom_scm_get_version(void)
315{ 354{
316 int context_id; 355 int context_id;
@@ -435,3 +474,21 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
435 return ret; 474 return ret;
436} 475}
437EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); 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/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index 95ef72a47b0f..d7a974d5f57c 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -1,4 +1,4 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. 1/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
2 * Copyright (C) 2015 Linaro Ltd. 2 * Copyright (C) 2015 Linaro Ltd.
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
@@ -16,6 +16,11 @@
16extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); 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); 17extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus);
18 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
19#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) 24#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
20 25
21extern u32 qcom_scm_get_version(void); 26extern u32 qcom_scm_get_version(void);