Message ID | 1425328207-58415-4-git-send-email-lina.iyer@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mar 2, 2015, at 2:30 PM, Lina Iyer <lina.iyer@linaro.org> wrote: > Support powering down the calling cpu, by trapping into SCM. This > termination function triggers the ARM cpu to execute WFI instruction, > causing the power controller to safely power the cpu down. > > Caches may be flushed before powering down the cpu. If cache controller > is set to turn off when the cpu is powered down, then the flags argument > indicates to the secure mode to flush its cache lines before executing > WFI.The warm boot reset address for the cpu should be set before the > calling into this function for the cpu to resume. > > The original code for the qcom_scm_call_atomic1() comes from a patch by > Stephen Boyd [1]. The function scm_call_atomic1() has been cherry picked > and renamed to match the convention used in this file. Since there are > no users of scm_call_atomic2(), the function is not included. > > [1]. https://lkml.org/lkml/2014/8/4/765 > > Signed-off-by: Stephen Boyd <sboyd@codeauraro.org> > Signed-off-by: Lina Iyer <lina.iyer@linaro.org> > --- > drivers/firmware/qcom_scm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ > include/linux/qcom_scm.h | 6 ++++- > 2 files changed, 62 insertions(+), 1 deletion(-) > > diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c > index 4d8ede4..994b50f 100644 > --- a/drivers/firmware/qcom_scm.c > +++ b/drivers/firmware/qcom_scm.c > @@ -311,6 +311,45 @@ out: > return ret; > } > > +#define SCM_CLASS_REGISTER (0x2 << 8) > +#define SCM_MASK_IRQS BIT(5) > +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ > + SCM_CLASS_REGISTER | \ > + SCM_MASK_IRQS | \ > + (n & 0xf)) > + > +/** > + * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument > + * @svc_id: service identifier > + * @cmd_id: command identifier > + * @arg1: first argument > + * > + * This shall only be used with commands that are guaranteed to be > + * uninterruptable, atomic and SMP safe. > + */ > +static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) > +{ > + int context_id; > + > + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); > + register u32 r1 asm("r1") = (u32)&context_id; > + register u32 r2 asm("r2") = arg1; > + > + asm volatile( > + __asmeq("%0", "r0") > + __asmeq("%1", "r0") > + __asmeq("%2", "r1") > + __asmeq("%3", "r2") > +#ifdef REQUIRES_SEC > + ".arch_extension sec\n" > +#endif > + "smc #0 @ switch to secure world\n" > + : "=r" (r0) > + : "r" (r0), "r" (r1), "r" (r2) > + : "r3"); > + return r0; > +} > + > u32 qcom_scm_get_version(void) > { > int context_id; > @@ -435,3 +474,21 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) > return ret; > } > EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); > + > +#define QCOM_SCM_CMD_TERMINATE_PC 0x2 > +#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 why is the mask 0x3 and not 0x1? > + > +/** > + * qcom_scm_cpu_power_down() - Power down the cpu > + * @flags - Flags to flush cache > + * > + * This is an end point to power down cpu. If there was a pending interrupt, > + * the control would return from this function, otherwise, the cpu jumps to the > + * warm boot entry point set for this cpu upon reset. > + */ > +void qcom_scm_cpu_power_down(u32 flags) > +{ > + qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC, > + flags & QCOM_SCM_FLUSH_FLAG_MASK); > +} > +EXPORT_SYMBOL(qcom_scm_cpu_power_down); > diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h > index 95ef72a..0f26f44 100644 > --- a/include/linux/qcom_scm.h > +++ b/include/linux/qcom_scm.h > @@ -1,4 +1,4 @@ > -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. > +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. > * Copyright (C) 2015 Linaro Ltd. > * > * This program is free software; you can redistribute it and/or modify > @@ -13,8 +13,12 @@ > #ifndef __QCOM_SCM_H > #define __QCOM_SCM_H > > +#define QCOM_SCM_L2_ON 0x0 > +#define QCOM_SCM_L2_OFF 0x1 One, can you move this below qcom_scm_set_warm_boot_addr so they are closer to qcom_scm_cpu_power_down. Also, we probably need to prefix the names to be clear the flags are associated with qcom_scm_cpu_power_down. QCOM_SCM_CPU_PWR_DOWN_{L2_ON,L2_OFF}? > + > extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); > extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); > +extern void qcom_scm_cpu_power_down(u32 flags); > > #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) > > -- > 2.1.0 > - k
On Mon, Mar 02 2015 at 13:52 -0700, Kumar Gala wrote: > >On Mar 2, 2015, at 2:30 PM, Lina Iyer <lina.iyer@linaro.org> wrote: > >> Support powering down the calling cpu, by trapping into SCM. This >> termination function triggers the ARM cpu to execute WFI instruction, >> causing the power controller to safely power the cpu down. >> >> Caches may be flushed before powering down the cpu. If cache controller >> is set to turn off when the cpu is powered down, then the flags argument >> indicates to the secure mode to flush its cache lines before executing >> WFI.The warm boot reset address for the cpu should be set before the >> calling into this function for the cpu to resume. >> >> The original code for the qcom_scm_call_atomic1() comes from a patch by >> Stephen Boyd [1]. The function scm_call_atomic1() has been cherry picked >> and renamed to match the convention used in this file. Since there are >> no users of scm_call_atomic2(), the function is not included. >> >> [1]. https://lkml.org/lkml/2014/8/4/765 >> >> Signed-off-by: Stephen Boyd <sboyd@codeauraro.org> >> Signed-off-by: Lina Iyer <lina.iyer@linaro.org> >> --- >> drivers/firmware/qcom_scm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ >> include/linux/qcom_scm.h | 6 ++++- >> 2 files changed, 62 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c >> index 4d8ede4..994b50f 100644 >> --- a/drivers/firmware/qcom_scm.c >> +++ b/drivers/firmware/qcom_scm.c >> @@ -311,6 +311,45 @@ out: >> return ret; >> } >> >> +#define SCM_CLASS_REGISTER (0x2 << 8) >> +#define SCM_MASK_IRQS BIT(5) >> +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ >> + SCM_CLASS_REGISTER | \ >> + SCM_MASK_IRQS | \ >> + (n & 0xf)) >> + >> +/** >> + * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument >> + * @svc_id: service identifier >> + * @cmd_id: command identifier >> + * @arg1: first argument >> + * >> + * This shall only be used with commands that are guaranteed to be >> + * uninterruptable, atomic and SMP safe. >> + */ >> +static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) >> +{ >> + int context_id; >> + >> + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); >> + register u32 r1 asm("r1") = (u32)&context_id; >> + register u32 r2 asm("r2") = arg1; >> + >> + asm volatile( >> + __asmeq("%0", "r0") >> + __asmeq("%1", "r0") >> + __asmeq("%2", "r1") >> + __asmeq("%3", "r2") >> +#ifdef REQUIRES_SEC >> + ".arch_extension sec\n" >> +#endif >> + "smc #0 @ switch to secure world\n" >> + : "=r" (r0) >> + : "r" (r0), "r" (r1), "r" (r2) >> + : "r3"); >> + return r0; >> +} >> + >> u32 qcom_scm_get_version(void) >> { >> int context_id; >> @@ -435,3 +474,21 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) >> return ret; >> } >> EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); >> + >> +#define QCOM_SCM_CMD_TERMINATE_PC 0x2 >> +#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 > >why is the mask 0x3 and not 0x1? > Per the secure code, this is a 2 bit entity, hence a 0x3 for the mask. >> + >> +/** >> + * qcom_scm_cpu_power_down() - Power down the cpu >> + * @flags - Flags to flush cache >> + * >> + * This is an end point to power down cpu. If there was a pending interrupt, >> + * the control would return from this function, otherwise, the cpu jumps to the >> + * warm boot entry point set for this cpu upon reset. >> + */ >> +void qcom_scm_cpu_power_down(u32 flags) >> +{ >> + qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC, >> + flags & QCOM_SCM_FLUSH_FLAG_MASK); >> +} >> +EXPORT_SYMBOL(qcom_scm_cpu_power_down); >> diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h >> index 95ef72a..0f26f44 100644 >> --- a/include/linux/qcom_scm.h >> +++ b/include/linux/qcom_scm.h >> @@ -1,4 +1,4 @@ >> -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. >> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. >> * Copyright (C) 2015 Linaro Ltd. >> * >> * This program is free software; you can redistribute it and/or modify >> @@ -13,8 +13,12 @@ >> #ifndef __QCOM_SCM_H >> #define __QCOM_SCM_H >> >> +#define QCOM_SCM_L2_ON 0x0 >> +#define QCOM_SCM_L2_OFF 0x1 > >One, can you move this below qcom_scm_set_warm_boot_addr so they are closer to qcom_scm_cpu_power_down. Also, we probably need to prefix the names to be clear the flags are associated with qcom_scm_cpu_power_down. QCOM_SCM_CPU_PWR_DOWN_{L2_ON,L2_OFF}? > Okay. >> + >> extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); >> extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); >> +extern void qcom_scm_cpu_power_down(u32 flags); >> >> #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) >> >> -- >> 2.1.0 >> > >- k > >-- >Qualcomm Innovation Center, Inc. >The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, >a Linux Foundation Collaborative Project >
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 4d8ede4..994b50f 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -311,6 +311,45 @@ out: return ret; } +#define SCM_CLASS_REGISTER (0x2 << 8) +#define SCM_MASK_IRQS BIT(5) +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) + +/** + * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + int context_id; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + : "r3"); + return r0; +} + u32 qcom_scm_get_version(void) { int context_id; @@ -435,3 +474,21 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) return ret; } EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); + +#define QCOM_SCM_CMD_TERMINATE_PC 0x2 +#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags - Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void qcom_scm_cpu_power_down(u32 flags) +{ + qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC, + flags & QCOM_SCM_FLUSH_FLAG_MASK); +} +EXPORT_SYMBOL(qcom_scm_cpu_power_down); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 95ef72a..0f26f44 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2015 Linaro Ltd. * * This program is free software; you can redistribute it and/or modify @@ -13,8 +13,12 @@ #ifndef __QCOM_SCM_H #define __QCOM_SCM_H +#define QCOM_SCM_L2_ON 0x0 +#define QCOM_SCM_L2_OFF 0x1 + extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); +extern void qcom_scm_cpu_power_down(u32 flags); #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))