diff options
-rw-r--r-- | arch/arm/mach-shmobile/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/common.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/r8a7779.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/platsmp.c | 16 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm-r8a7779.c | 30 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/smp-r8a7779.c | 153 |
6 files changed, 201 insertions, 8 deletions
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index 997bd2bcd5f..7ad6954c46c 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -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 := diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index 44ce124bfde..e4b945e271e 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h @@ -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 */ diff --git a/arch/arm/mach-shmobile/include/mach/r8a7779.h b/arch/arm/mach-shmobile/include/mach/r8a7779.h index e6a6166fdf2..b07ad318eb2 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7779.h +++ b/arch/arm/mach-shmobile/include/mach/r8a7779.h @@ -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; diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c index 3f3325d4206..993381257f6 100644 --- a/arch/arm/mach-shmobile/platsmp.c +++ b/arch/arm/mach-shmobile/platsmp.c @@ -22,12 +22,16 @@ #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; } diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c index d9c56fe067a..c38ba7b43ef 100644 --- a/arch/arm/mach-shmobile/pm-r8a7779.c +++ b/arch/arm/mach-shmobile/pm-r8a7779.c @@ -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 index 00000000000..cc97ef892d1 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -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); +} |