Thread (66 messages) flat view 66 messages, 4 authors, 2011-03-01
STALE5680d

[PATCH 4/5] ARM: scu: Move register defines to header file

From: Russell King - ARM Linux <hidden>
Date: 2011-01-25 12:29:24
Also in: linux-omap
Subsystem: arm port, the rest · Maintainers: Russell King, Linus Torvalds

On Tue, Jan 25, 2011 at 12:16:56PM +0000, Russell King - ARM Linux wrote:
On Tue, Jan 25, 2011 at 05:32:57PM +0530, Santosh Shilimkar wrote:
quoted
I have similar patch implemented but what it does is just
prepares the value to be programmed and stores it to a scratchpad.
I see your point but below patch may now work well. The reason is the SCU
register needs to accessed at the lowest level. May be even when
stack is not available. Also this register needs to be cleared in cases
where the attempted power state is failed for some reason.
quoted
From the documentation I have, the power control register has no effect
until the CPU enters WFI mode - which means that provided we can
guarantee no one issues a WFI instruction between setting the power mode,
and executing that instruction, being woken up (or failing) and resetting
the power mode back... that shouldn't require the power mode to be
programmed from assembly code.

In any case, we actually need the help of spinlocks to deal with
concurrent access to the SCU power control register - something you
can't do in assembly code.

On the way down to a WFI low power mode, we can call scu_power_mode(),
do the rest of the cleanup (which must not schedule) and issue WFI.  On
the way back up, do whatever needs to be done and call scu_power_mode()
to reset back to 'normal' mode.
quoted
Also note that this register can be blocked using trust-zone which
again makes it platform dependent because direct write won't be
allowed in that case.
Yes, I did notice.  What's the OMAP SMC interface for that?
quoted
I would prefer the header export if there is no major
objection since there is already a possibility of custom
implementation with trust zone.

On OMAP4, on ES1.0 we need to use a secure API to achieve
this where as on ES2.0, it can be directly accessed.
As I say, I'd rather not have each SoC implementing access to this as
someone's bound to forget the spinlock if they're dealing with more than
one CPU, which'll make stuff unreliable (just like I did on my initial
version.)

We could have a callback to SoC code which does the appropriate SMC call,
but first I'll need to know what's required for the SMC call.
I did mean to include the updated patch:

 arch/arm/include/asm/smp_scu.h |    5 +++++
 arch/arm/kernel/smp_scu.c      |   23 +++++++++++++++++++++++
 2 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/arch/arm/include/asm/smp_scu.h b/arch/arm/include/asm/smp_scu.h
index 2376835..108f31d 100644
--- a/arch/arm/include/asm/smp_scu.h
+++ b/arch/arm/include/asm/smp_scu.h
@@ -1,7 +1,12 @@
 #ifndef __ASMARM_ARCH_SCU_H
 #define __ASMARM_ARCH_SCU_H
 
+#define SCU_PM_NORMAL	0
+#define SCU_PM_DORMANT	2
+#define SCU_PM_POWEROFF	3
+
 unsigned int scu_get_core_count(void __iomem *);
 void scu_enable(void __iomem *);
+int scu_power_mode(unsigned int);
 
 #endif
diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c
index 9ab4149..4c4c90d 100644
--- a/arch/arm/kernel/smp_scu.c
+++ b/arch/arm/kernel/smp_scu.c
@@ -10,6 +10,7 @@
  */
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/spinlock.h>
 
 #include <asm/smp_scu.h>
 #include <asm/cacheflush.h>
@@ -20,6 +21,8 @@
 #define SCU_INVALIDATE		0x0c
 #define SCU_FPGA_REVISION	0x10
 
+static DEFINE_SPINLOCK(scu_power_lock);
+
 /*
  * Get the number of CPU cores from the SCU configuration
  */
@@ -50,3 +53,23 @@ void __init scu_enable(void __iomem *scu_base)
 	 */
 	flush_cache_all();
 }
+
+int scu_power_mode(unsigned int mode)
+{
+	int cpu = smp_processor_id();
+	int shift;
+	u32 val;
+
+	if (mode > 3 || mode == 1 || cpu > 3)
+		return -EINVAL;
+
+	shift = cpu * 8;
+
+	spin_lock(&scu_power_lock);
+	val = raw_readl(scu_base + SCU_CPU_STATUS) & ~(0x03 << shift);
+	val |= mode << shift;
+	__raw_writel(val, scu_base + SCU_CPU_STATUS);
+	spin_unlock(&scu_power_lock);
+
+	return 0;
+}
If we need a SMC call, that should happen within the spinlock'd region via
a function pointer which the platform can set.  I can't code that into this
patch until I know what the SMC call requires as arguments (cpu number,
power state, or new register value, or mask and bits to set?)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help