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

Russell King - ARM Linux
2011-01-25T12:29:40Z

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:
> > 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.
>
> >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.
>
> > 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?
>
> > 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?)