ARM: mach-shmobile: r8a7779 SMP support V3
authorMagnus Damm <damm@opensource.se>
Tue, 10 Jan 2012 08:44:39 +0000 (17:44 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Thu, 12 Jan 2012 02:00:27 +0000 (11:00 +0900)
This patch contains r8a7779 SMP support V3 - now including
CPU hotplug offine and online support. The r8a7779 power
domain code is tied together with SMP glue code which allows
us to control the power domains via CPU hotplug.

At this point the kernel boots with the 4 Cortex-A9 cores in
SMP mode and all CPU cores except CPU0 can be hotplugged.

The code in platsmp.c is quite far from pretty, but it is
kept like that intentionally to avoid creating layers of
code that will go away in the near future anyway. The code
needs to be updated when some per-SoC handling code will be
added to the ARM architecture, see the following patch for
more information:
 "[RFC PATCH 0/3] Per SoC descriptor"

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/include/mach/r8a7779.h
arch/arm/mach-shmobile/platsmp.c
arch/arm/mach-shmobile/pm-r8a7779.c
arch/arm/mach-shmobile/smp-r8a7779.c [new file with mode: 0644]

index 997bd2bcd5f669459d84e0b33a461da95802edca..7ad6954c46cd2e5780528808bdfc20d921a2aa0d 100644 (file)
@@ -18,6 +18,7 @@ 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
+smp-$(CONFIG_ARCH_R8A7779)     += smp-r8a7779.o
 
 # Pinmux setup
 pfc-y                          :=
index 44ce124bfdec6f019bb97f63644e74179111cefb..e4b945e271e73db84b5fa608ce4eba82267d13dc 100644 (file)
@@ -68,4 +68,10 @@ extern void r8a7779_clock_init(void);
 extern void r8a7779_pinmux_init(void);
 extern void r8a7779_pm_init(void);
 
+extern unsigned int r8a7779_get_core_count(void);
+extern int r8a7779_platform_cpu_kill(unsigned int cpu);
+extern void r8a7779_secondary_init(unsigned int cpu);
+extern int r8a7779_boot_secondary(unsigned int cpu);
+extern void r8a7779_smp_prepare_cpus(void);
+
 #endif /* __ARCH_MACH_COMMON_H */
index e6a6166fdf2224b4535f9cc2d8c1bc8259ca058c..b07ad318eb2ec47577b2893bd9e8a7ba71c39d3d 100644 (file)
@@ -343,6 +343,9 @@ static inline struct r8a7779_pm_ch *to_r8a7779_ch(struct generic_pm_domain *d)
        return &container_of(d, struct r8a7779_pm_domain, genpd)->ch;
 }
 
+extern int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch);
+extern int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch);
+
 #ifdef CONFIG_PM
 extern struct r8a7779_pm_domain r8a7779_sh4a;
 extern struct r8a7779_pm_domain r8a7779_sgx;
index 3f3325d4206d8592ab861ee951267aa83ebeaa3f..993381257f69a6352adcc7e61df3d92d65b88643 100644 (file)
 #include <mach/common.h>
 
 #define is_sh73a0() (machine_is_ag5evm() || machine_is_kota2())
+#define is_r8a7779() machine_is_marzen()
 
 static unsigned int __init shmobile_smp_get_core_count(void)
 {
        if (is_sh73a0())
                return sh73a0_get_core_count();
 
+       if (is_r8a7779())
+               return r8a7779_get_core_count();
+
        return 1;
 }
 
@@ -35,10 +39,16 @@ static void __init shmobile_smp_prepare_cpus(void)
 {
        if (is_sh73a0())
                sh73a0_smp_prepare_cpus();
+
+       if (is_r8a7779())
+               r8a7779_smp_prepare_cpus();
 }
 
 int shmobile_platform_cpu_kill(unsigned int cpu)
 {
+       if (is_r8a7779())
+               return r8a7779_platform_cpu_kill(cpu);
+
        return 1;
 }
 
@@ -48,6 +58,9 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
 
        if (is_sh73a0())
                sh73a0_secondary_init(cpu);
+
+       if (is_r8a7779())
+               r8a7779_secondary_init(cpu);
 }
 
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -55,6 +68,9 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
        if (is_sh73a0())
                return sh73a0_boot_secondary(cpu);
 
+       if (is_r8a7779())
+               return r8a7779_boot_secondary(cpu);
+
        return -ENOSYS;
 }
 
index d9c56fe067a541cd88bee9809ef3e4195a092e66..c38ba7b43ef83e2962eaf61d3cec6d7afbed3b8a 100644 (file)
@@ -48,7 +48,9 @@ static void __iomem *r8a7779_sysc_base;
 #define SYSCISR_RETRIES 1000
 #define SYSCISR_DELAY_US 1
 
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM) || defined(CONFIG_SMP)
+
+static DEFINE_SPINLOCK(r8a7779_sysc_lock); /* SMP CPUs + I/O devices */
 
 static int r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch *r8a7779_ch,
                                   int sr_bit, int reg_offs)
@@ -86,9 +88,12 @@ static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
        unsigned int isr_mask = 1 << r8a7779_ch->isr_bit;
        unsigned int chan_mask = 1 << r8a7779_ch->chan_bit;
        unsigned int status;
+       unsigned long flags;
        int ret = 0;
        int k;
 
+       spin_lock_irqsave(&r8a7779_sysc_lock, flags);
+
        iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
 
        do {
@@ -112,6 +117,8 @@ static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
        iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
 
  out:
+       spin_unlock_irqrestore(&r8a7779_sysc_lock, flags);
+
        pr_debug("r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d\n",
                 r8a7779_ch->isr_bit, ioread32(r8a7779_sysc_base + PWRSR0),
                 ioread32(r8a7779_sysc_base + PWRSR1),
@@ -121,12 +128,12 @@ static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
        return ret;
 }
 
-static int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
+int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
 {
        return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_off);
 }
 
-static int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
+int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
 {
        return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_on);
 }
@@ -142,6 +149,14 @@ static void __init r8a7779_sysc_init(void)
        iowrite32(0, r8a7779_sysc_base + SYSCIMR);
 }
 
+#else /* CONFIG_PM || CONFIG_SMP */
+
+static inline void r8a7779_sysc_init(void) {}
+
+#endif /* CONFIG_PM || CONFIG_SMP */
+
+#ifdef CONFIG_PM
+
 static int pd_power_down(struct generic_pm_domain *genpd)
 {
        return r8a7779_sysc_power_down(to_r8a7779_ch(genpd));
@@ -223,13 +238,12 @@ struct r8a7779_pm_domain r8a7779_impx3 = {
        }
 };
 
-#else /* CONFIG_PM */
-
-static inline void r8a7779_sysc_init(void) {}
-
 #endif /* CONFIG_PM */
 
 void __init r8a7779_pm_init(void)
 {
-       r8a7779_sysc_init();
+       static int once;
+
+       if (!once++)
+               r8a7779_sysc_init();
 }
diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c
new file mode 100644 (file)
index 0000000..cc97ef8
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * SMP support for R-Mobile / SH-Mobile - r8a7779 portion
+ *
+ * Copyright (C) 2011  Renesas Solutions Corp.
+ * Copyright (C) 2011  Magnus Damm
+ *
+ * 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 <linux/delay.h>
+#include <mach/common.h>
+#include <mach/r8a7779.h>
+#include <asm/smp_scu.h>
+#include <asm/smp_twd.h>
+#include <asm/hardware/gic.h>
+
+#define AVECR 0xfe700040
+
+static struct r8a7779_pm_ch r8a7779_ch_cpu1 = {
+       .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
+       .chan_bit = 1, /* ARM1 */
+       .isr_bit = 1, /* ARM1 */
+};
+
+static struct r8a7779_pm_ch r8a7779_ch_cpu2 = {
+       .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
+       .chan_bit = 2, /* ARM2 */
+       .isr_bit = 2, /* ARM2 */
+};
+
+static struct r8a7779_pm_ch r8a7779_ch_cpu3 = {
+       .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
+       .chan_bit = 3, /* ARM3 */
+       .isr_bit = 3, /* ARM3 */
+};
+
+static struct r8a7779_pm_ch *r8a7779_ch_cpu[4] = {
+       [1] = &r8a7779_ch_cpu1,
+       [2] = &r8a7779_ch_cpu2,
+       [3] = &r8a7779_ch_cpu3,
+};
+
+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 r8a7779_get_core_count(void)
+{
+       void __iomem *scu_base = scu_base_addr();
+
+#ifdef CONFIG_HAVE_ARM_TWD
+       /* twd_base needs to be initialized before percpu_timer_setup() */
+       twd_base = (void __iomem *)0xf0000600;
+#endif
+
+       return scu_get_core_count(scu_base);
+}
+
+int r8a7779_platform_cpu_kill(unsigned int cpu)
+{
+       struct r8a7779_pm_ch *ch = NULL;
+       int ret = -EIO;
+
+       cpu = cpu_logical_map(cpu);
+
+       /* disable cache coherency */
+       modify_scu_cpu_psr(3 << (cpu * 8), 0);
+
+       if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))
+               ch = r8a7779_ch_cpu[cpu];
+
+       if (ch)
+               ret = r8a7779_sysc_power_down(ch);
+
+       return ret ? ret : 1;
+}
+
+void __cpuinit r8a7779_secondary_init(unsigned int cpu)
+{
+       gic_secondary_init(0);
+}
+
+int __cpuinit r8a7779_boot_secondary(unsigned int cpu)
+{
+       struct r8a7779_pm_ch *ch = NULL;
+       int ret = -EIO;
+
+       cpu = cpu_logical_map(cpu);
+
+       /* enable cache coherency */
+       modify_scu_cpu_psr(0, 3 << (cpu * 8));
+
+       if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))
+               ch = r8a7779_ch_cpu[cpu];
+
+       if (ch)
+               ret = r8a7779_sysc_power_up(ch);
+
+       return ret;
+}
+
+void __init r8a7779_smp_prepare_cpus(void)
+{
+       int cpu = cpu_logical_map(0);
+
+       scu_enable(scu_base_addr());
+
+       /* Map the reset vector (in headsmp.S) */
+       __raw_writel(__pa(shmobile_secondary_vector), __io(AVECR));
+
+       /* enable cache coherency on CPU0 */
+       modify_scu_cpu_psr(0, 3 << (cpu * 8));
+
+       r8a7779_pm_init();
+
+       /* power off secondary CPUs */
+       r8a7779_platform_cpu_kill(1);
+       r8a7779_platform_cpu_kill(2);
+       r8a7779_platform_cpu_kill(3);
+}