Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753327Ab3DMNIQ (ORCPT ); Sat, 13 Apr 2013 09:08:16 -0400 Received: from mail-ee0-f45.google.com ([74.125.83.45]:42580 "EHLO mail-ee0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753074Ab3DMNIN (ORCPT ); Sat, 13 Apr 2013 09:08:13 -0400 MIME-Version: 1.0 In-Reply-To: <1365651547-11583-1-git-send-email-zhangwm@marvell.com> References: <1365651547-11583-1-git-send-email-zhangwm@marvell.com> Date: Sat, 13 Apr 2013 21:08:12 +0800 Message-ID: Subject: Re: [PATCH 4/4] ARM: mmp: add SMP support for pxa988 From: Haojian Zhuang To: Neil Zhang Cc: Grant Likely , "linux-arm-kernel@lists.infradead.org" , "linux-kernel@vger.kernel.org" , Chao Xie Content-Type: text/plain; charset=ISO-8859-1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15119 Lines: 464 On Thu, Apr 11, 2013 at 11:39 AM, Neil Zhang wrote: > Add SMP support for pxa988. > > Signed-off-by: Neil Zhang > Signed-off-by: Chao Xie > --- > arch/arm/mach-mmp/Makefile | 4 + > arch/arm/mach-mmp/common.h | 2 + > arch/arm/mach-mmp/headsmp.S | 104 ++++++++++++++++++++++++++ > arch/arm/mach-mmp/mmpx-dt.c | 1 + > arch/arm/mach-mmp/platsmp.c | 170 +++++++++++++++++++++++++++++++++++++++++++ > arch/arm/mach-mmp/reset.c | 66 +++++++++++++++++ > arch/arm/mach-mmp/reset.h | 29 +++++++ > 7 files changed, 376 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-mmp/headsmp.S > create mode 100644 arch/arm/mach-mmp/platsmp.c > create mode 100644 arch/arm/mach-mmp/reset.c > create mode 100644 arch/arm/mach-mmp/reset.h > > diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile > index b60065f..e7b6173 100644 > --- a/arch/arm/mach-mmp/Makefile > +++ b/arch/arm/mach-mmp/Makefile > @@ -9,6 +9,10 @@ obj-$(CONFIG_CPU_PXA168) += pxa168.o > obj-$(CONFIG_CPU_PXA910) += pxa910.o > obj-$(CONFIG_CPU_MMP2) += mmp2.o sram.o > > +ifeq ($(CONFIG_SMP),y) > +obj-$(CONFIG_CPU_PXA988) += platsmp.o headsmp.o reset.o > +endif > + You already select CONFIG_SMP if PXA988 is selected. There's no reason to check CONFIG_SMP in Makefile again. > ifeq ($(CONFIG_COMMON_CLK), ) > obj-y += clock.o > obj-$(CONFIG_CPU_PXA168) += clock-pxa168.o > diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h > index 8c9b510..7496cd0 100644 > --- a/arch/arm/mach-mmp/common.h > +++ b/arch/arm/mach-mmp/common.h > @@ -1,5 +1,7 @@ > #define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) > > +extern struct smp_operations mmp_smp_ops; > + > extern void timer_init(int irq); > > extern void __init icu_init_irq(void); > diff --git a/arch/arm/mach-mmp/headsmp.S b/arch/arm/mach-mmp/headsmp.S > new file mode 100644 > index 0000000..2b6177e > --- /dev/null > +++ b/arch/arm/mach-mmp/headsmp.S > @@ -0,0 +1,104 @@ > +/* > + * linux/arch/arm/mach-mmp/headsmp.S > + * > + * Copyright (C) 2012 Marvell, Inc. > + * > + * Author: Neil Zhang > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + * > + */ > +#include > +#include > +#include > +#include > +#include > +#include > + > + __CPUINIT > + > +/* > + * Marvell specific entry point for secondary CPUs. > + * The secondary kernel init calls v7_flush_dcache_all before it enables > + * the L1; however, the L1 comes out of reset in an undefined state, so > + * the clean + invalidate performed by v7_flush_dcache_all causes a bunch > + * of cache lines with uninitialized data and uninitialized tags to get > + * written out to memory, which does really unpleasant things to the main > + * processor. We fix this by performing an invalidate, rather than a > + * clean + invalidate for secondary core, before jumping into the kernel. > + * > + * This funciton is cloned from arch/arm/mach-tegra/headsmp.S, and needs > + * to be called for both secondary cores startup and primary core resume > + * procedures. > + */ > + .align L1_CACHE_SHIFT > + > + > +/* > + * PXA specific entry point for secondary CPUs. This provides > + * a "holding pen" into which all secondary cores are held until we're > + * ready for them to initialise. > + */ > +ENTRY(mmp_secondary_startup) > + mrc p15, 0, r0, c0, c0, 5 > + and r0, r0, #15 > + adr r4, 1f > + ldmia r4, {r5, r6} > + sub r4, r4, r5 > + add r6, r6, r4 > +pen: ldr r7, [r6] > + cmp r7, r0 > + bne pen > + > + /* > + * we've been released from the holding pen: secondary_stack > + * should now contain the SVC stack for this core > + */ > + bl v7_invalidate_l1 > + b secondary_startup > +ENDPROC(mmp_secondary_startup) > + > + .align 2 > +1: .long . > + .long pen_release > + > + > +/* > + * Note: The following code is located into the .data section. This is to > + * allow sw_reset_flag and cpu_plugin_handler to be accessed with a > + * relative load while we can't rely on any MMU translation. > + * Reference from: arch/arm/kernel/sleep.S > + */ > + > + .data > + .align > + > +/* > + * ROM code jumps to this function while waking up from CPU > + * OFF or software reset state. Physical address of the function is > + * stored at CA9_WARM_RESET_VECTOR while system is bring up. > + */ > +ENTRY(mmp_cpu_reset_entry) > + adr r1, mmp_entry_vectors > + mrc p15, 0, r0, c0, c0, 5 > + and r0, r0, #15 @ fetch CPUID > +1: > + ldr r2, [r1, r0, lsl #2] @ get the handler addr for this core > + cmp r2, #0 > + movne pc, r2 @ jump to the handler > + beq 1b > +ENDPROC(mmp_cpu_reset_entry) > + > + /* Point to the address that save handlers for each core */ > + .global mmp_entry_vectors > +mmp_entry_vectors: > + .rept CONFIG_NR_CPUS > + .long 0 @ preserve stack phys ptr here > + .endr > diff --git a/arch/arm/mach-mmp/mmpx-dt.c b/arch/arm/mach-mmp/mmpx-dt.c > index bbc0c50..a5f5501 100644 > --- a/arch/arm/mach-mmp/mmpx-dt.c > +++ b/arch/arm/mach-mmp/mmpx-dt.c > @@ -71,6 +71,7 @@ static const char *pxa988_dt_board_compat[] __initdata = { > }; > > DT_MACHINE_START(PXA988_DT, "Marvell PXA988 (Device Tree Support)") > + .smp = smp_ops(mmp_smp_ops), > .map_io = mmp_map_io, > .init_irq = pxa988_dt_gic_init, > .init_time = pxa988_dt_init_timer, > diff --git a/arch/arm/mach-mmp/platsmp.c b/arch/arm/mach-mmp/platsmp.c > new file mode 100644 > index 0000000..a6590a3 > --- /dev/null > +++ b/arch/arm/mach-mmp/platsmp.c > @@ -0,0 +1,170 @@ > +/* > + * linux/arch/arm/mach-mmp/platsmp.c > + * > + * Copyright (C) 2002 ARM Ltd. > + * All Rights Reserved > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#include "common.h" > +#include "reset.h" > + > +/* > + * Write pen_release in a way that is guaranteed to be visible to all > + * observers, irrespective of whether they're taking part in coherency > + * or not. This is necessary for the hotplug code to work reliably. > + */ > +static void __cpuinit write_pen_release(int val) > +{ > + pen_release = val; > + smp_wmb(); > + __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); > + outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); > +} > + > +#ifdef CONFIG_HAVE_ARM_SCU > +static void __iomem *scu_get_base_addr(void) > +{ > + return SCU_VIRT_BASE; > +} > +#endif > + > +static inline unsigned int get_core_count(void) > +{ > + u32 ret = 1; > +#ifdef CONFIG_HAVE_ARM_SCU > + ret = scu_get_core_count(scu_get_base_addr()); > +#endif > + > + return ret; > +} > + > +static DEFINE_SPINLOCK(boot_lock); > + > +static void __cpuinit mmp_secondary_init(unsigned int cpu) > +{ > + /* > + * if any interrupts are already enabled for the primary > + * core (e.g. timer irq), then they will not have been enabled > + * for us: do so > + */ > + gic_secondary_init(0); > + > + /* > + * let the primary processor know we're out of the > + * pen, then head off into the C entry point > + */ > + write_pen_release(-1); > + > + /* > + * Synchronise with the boot thread. > + */ > + spin_lock(&boot_lock); > + spin_unlock(&boot_lock); Lock & unlock without protecting anything. If so, you can remove this. > +} > + > +static int __cpuinit mmp_boot_secondary(unsigned int cpu, struct task_struct *idle) > +{ > + unsigned long timeout; > + > + /* > + * Avoid timer calibration on slave cpus. Use the value calibrated > + * on master cpu. Referenced from tegra3 > + */ > + preset_lpj = loops_per_jiffy; > + > + /* > + * set synchronisation state between this boot processor > + * and the secondary one > + */ > + > + spin_lock(&boot_lock); > + > + /* > + * The secondary processor is waiting to be released from > + * the holding pen - release it, then wait for it to flag > + * that it has been released by resetting pen_release. > + * > + * Note that "pen_release" is the hardware CPU ID, whereas > + * "cpu" is Linux's internal ID. > + */ > + write_pen_release(cpu); > + > + /* reset the cpu, let it branch to the kernel entry */ > + mmp_cpu_power_up(cpu); > + > + timeout = jiffies + (1 * HZ); > + while (time_before(jiffies, timeout)) { > + smp_rmb(); > + if (pen_release == -1) > + break; > + > + udelay(10); > + } > + > + /* > + * now the secondary core is starting up let it run its > + * calibrations, then wait for it to finish > + */ > + spin_unlock(&boot_lock); > + > + return pen_release != -1 ? -ENOSYS : 0; > +} > + > +/* > + * Initialise the CPU possible map early - this describes the CPUs > + * which may be present or become present in the system. > + */ > +static void __init mmp_smp_init_cpus(void) > +{ > + unsigned int i, ncores = get_core_count(); > + > + for (i = 0; i < ncores; i++) > + set_cpu_possible(i, true); > +} > + > +static void __init mmp_smp_prepare_cpus(unsigned int max_cpus) > +{ > + int i; > + > + /* > + * Initialise the present map, which describes the set of CPUs > + * actually populated at the present time. > + */ > + for (i = 0; i < max_cpus; i++) > + set_cpu_present(i, true); > + > +#ifdef CONFIG_HAVE_ARM_SCU > + scu_enable(scu_get_base_addr()); > +#endif > + > + mmp_entry_vector_init(); > +} > + > +struct smp_operations mmp_smp_ops __initdata = { > + .smp_init_cpus = mmp_smp_init_cpus, > + .smp_prepare_cpus = mmp_smp_prepare_cpus, > + .smp_secondary_init = mmp_secondary_init, > + .smp_boot_secondary = mmp_boot_secondary, > +}; > diff --git a/arch/arm/mach-mmp/reset.c b/arch/arm/mach-mmp/reset.c > new file mode 100644 > index 0000000..b47e400 > --- /dev/null > +++ b/arch/arm/mach-mmp/reset.c > @@ -0,0 +1,66 @@ > +/* > + * linux/arch/arm/mach-mmp/reset.c > + * > + * Author: Neil Zhang > + * Copyright: (C) 2012 Marvell International Ltd. > + * > + * 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; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include > +#include > + > +#include > +#include > +#include > + > +#include > + > +#include "reset.h" > + > +#define PMU_CC2_AP APMU_REG(0x0100) > +#define CIU_CA9_WARM_RESET_VECTOR CIU_REG(0x00d8) > + > +/* > + * This function is called from boot_secondary to bootup the secondary cpu. > + */ > +void mmp_cpu_power_up(u32 cpu) > +{ > + u32 tmp; > + > + BUG_ON(cpu == 0); > + > + tmp = readl(PMU_CC2_AP); > + if (tmp & CPU_CORE_RST(cpu)) { > + /* Release secondary core from reset */ > + tmp &= ~(CPU_CORE_RST(cpu) > + | CPU_DBG_RST(cpu) | CPU_WDOG_RST(cpu)); > + writel(tmp, PMU_CC2_AP); > + } > +} > + > +void mmp_set_entry_vector(u32 cpu, u32 addr) > +{ > + BUG_ON(cpu >= CONFIG_NR_CPUS); > + > + mmp_entry_vectors[cpu] = addr; > + smp_wmb(); > + __cpuc_flush_dcache_area((void *)&mmp_entry_vectors[cpu], > + sizeof(mmp_entry_vectors[cpu])); > + outer_clean_range(__pa(&mmp_entry_vectors[cpu]), > + __pa(&mmp_entry_vectors[cpu + 1])); > +} > + > +void __init mmp_entry_vector_init(void) > +{ > + int cpu; > + > + /* We will reset from DDR directly by default */ > + writel(__pa(mmp_cpu_reset_entry), CIU_CA9_WARM_RESET_VECTOR); > + > + for (cpu = 1; cpu < CONFIG_NR_CPUS; cpu++) > + mmp_set_entry_vector(cpu, __pa(mmp_secondary_startup)); > +} > diff --git a/arch/arm/mach-mmp/reset.h b/arch/arm/mach-mmp/reset.h > new file mode 100644 > index 0000000..78d5486 > --- /dev/null > +++ b/arch/arm/mach-mmp/reset.h > @@ -0,0 +1,29 @@ > +/* > + * linux/arch/arm/mach-mmp/include/mach/reset.h > + * > + * Author: Neil Zhang > + * Copyright: (C) 2012 Marvell International Ltd. > + * > + * 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; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#ifndef __RESET_PXA988_H__ > +#define __RESET_PXA988_H__ > + > +#define CPU_CORE_RST(n) (1 << ((n) * 4 + 16)) > +#define CPU_DBG_RST(n) (1 << ((n) * 4 + 18)) > +#define CPU_WDOG_RST(n) (1 << ((n) * 4 + 19)) > + > +extern u32 mmp_entry_vectors[CONFIG_NR_CPUS]; > + > +void mmp_secondary_startup(void); > +void mmp_cpu_reset_entry(void); > + > +void mmp_cpu_power_up(u32 cpu); > +void mmp_set_entry_vector(u32 cpu, u32 addr); > +void __init mmp_entry_vector_init(void); > + > +#endif /* __RESET_PXA988_H__ */ > -- > 1.7.4.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/