ARM: mach-shmobile: sh73a0 SMP support
authorMagnus Damm <damm@opensource.se>
Tue, 14 Dec 2010 07:57:11 +0000 (16:57 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 14 Dec 2010 09:15:47 +0000 (18:15 +0900)
Add SMP support for ag5evm and the sh73a0 processor.

Onlining and offlining works well, but at this point
offlined processor cores are not put into sleep mode.

There is no spinlock for syncing the secondary core
with the first one in this implementation. The code
instead relies on the cpu_online() check in __cpu_up().

Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/arm/mach-shmobile/Makefile
arch/arm/mach-shmobile/include/mach/common.h
arch/arm/mach-shmobile/platsmp.c
arch/arm/mach-shmobile/smp-sh73a0.c [new file with mode: 0644]

index d4346ddd9121993609a423a5941f21ed507b8bf4..995f39bde90910c52a990896b1f22a2627912cd1 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_ARCH_SH73A0)     += setup-sh73a0.o clock-sh73a0.o
 smp-y                          := platsmp.o headsmp.o
 smp-$(CONFIG_HOTPLUG_CPU)      += hotplug.o
 smp-$(CONFIG_LOCAL_TIMERS)     += localtimer.o
+smp-$(CONFIG_ARCH_SH73A0)      += smp-sh73a0.o
 
 # Pinmux setup
 pfc-y                          :=
index 32822f7556ea94e6bc5ade7b45c009e435ebe725..b8f1f62bce979e1f672e9e4a7f45f489ee8907c2 100644 (file)
@@ -38,4 +38,9 @@ extern void sh73a0_pinmux_init(void);
 extern struct clk sh73a0_extal1_clk;
 extern struct clk sh73a0_extal2_clk;
 
+extern unsigned int sh73a0_get_core_count(void);
+extern void sh73a0_secondary_init(unsigned int cpu);
+extern int sh73a0_boot_secondary(unsigned int cpu);
+extern void sh73a0_smp_prepare_cpus(void);
+
 #endif /* __ARCH_MACH_COMMON_H */
index b41f5d0650e4074aa0f51cceb0f61874d8283e2e..3c2c0f49073ee2f8c0ae316ee886577d3e5e6120 100644 (file)
 #include <linux/smp.h>
 #include <linux/io.h>
 #include <asm/localtimer.h>
+#include <asm/mach-types.h>
+#include <mach/common.h>
 
 static unsigned int __init shmobile_smp_get_core_count(void)
 {
+       if (machine_is_ag5evm())
+               return sh73a0_get_core_count();
+
        return 1;
 }
 
 static void __init shmobile_smp_prepare_cpus(void)
 {
-       /* do nothing for now */
+       if (machine_is_ag5evm())
+               sh73a0_smp_prepare_cpus();
 }
 
 
 void __cpuinit platform_secondary_init(unsigned int cpu)
 {
        trace_hardirqs_off();
+
+       if (machine_is_ag5evm())
+               sh73a0_secondary_init(cpu);
 }
 
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 {
+       if (machine_is_ag5evm())
+               return sh73a0_boot_secondary(cpu);
+
        return -ENOSYS;
 }
 
diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c
new file mode 100644 (file)
index 0000000..4e71fd4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * SMP support for R-Mobile / SH-Mobile - sh73a0 portion
+ *
+ * Copyright (C) 2010  Magnus Damm
+ * Copyright (C) 2010  Takashi Yoshii
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <mach/common.h>
+#include <asm/smp_scu.h>
+#include <asm/smp_twd.h>
+#include <asm/hardware/gic.h>
+
+#define WUPCR          0xe6151010
+#define SRESCR         0xe6151018
+#define PSTR           0xe6151040
+#define SBAR            0xe6180020
+#define APARMBAREA      0xe6f10020
+
+static void __iomem *scu_base_addr(void)
+{
+       return (void __iomem *)0xf0000000;
+}
+
+static DEFINE_SPINLOCK(scu_lock);
+static unsigned long tmp;
+
+static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
+{
+       void __iomem *scu_base = scu_base_addr();
+
+       spin_lock(&scu_lock);
+       tmp = __raw_readl(scu_base + 8);
+       tmp &= ~clr;
+       tmp |= set;
+       spin_unlock(&scu_lock);
+
+       /* disable cache coherency after releasing the lock */
+       __raw_writel(tmp, scu_base + 8);
+}
+
+unsigned int __init sh73a0_get_core_count(void)
+{
+       void __iomem *scu_base = scu_base_addr();
+
+       return scu_get_core_count(scu_base);
+}
+
+void __cpuinit sh73a0_secondary_init(unsigned int cpu)
+{
+       gic_cpu_init(0, __io(0xf0000100));
+}
+
+int __cpuinit sh73a0_boot_secondary(unsigned int cpu)
+{
+       /* enable cache coherency */
+       modify_scu_cpu_psr(0, 3 << (cpu * 8));
+
+       if (((__raw_readw(__io(PSTR)) >> (4 * cpu)) & 3) == 3)
+               __raw_writel(1 << cpu, __io(WUPCR));    /* wake up */
+       else
+               __raw_writel(1 << cpu, __io(SRESCR));   /* reset */
+
+       return 0;
+}
+
+void __init sh73a0_smp_prepare_cpus(void)
+{
+#ifdef CONFIG_HAVE_ARM_TWD
+       twd_base = (void __iomem *)0xf0000600;
+#endif
+
+       scu_enable(scu_base_addr());
+
+       /* Map the reset vector (in headsmp.S) */
+       __raw_writel(0, __io(APARMBAREA));      /* 4k */
+       __raw_writel(__pa(shmobile_secondary_vector), __io(SBAR));
+
+       /* enable cache coherency on CPU0 */
+       modify_scu_cpu_psr(0, 3 << (0 * 8));
+}