2013-04-11 03:39:17

by Neil Zhang

[permalink] [raw]
Subject: [PATCH 4/4] ARM: mmp: add SMP support for pxa988

Add SMP support for pxa988.

Signed-off-by: Neil Zhang <[email protected]>
Signed-off-by: Chao Xie <[email protected]>
---
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
+
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 <[email protected]>
+ *
+ * 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 <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/memory.h>
+#include <asm/cache.h>
+#include <asm/assembler.h>
+#include <mach/addr-map.h>
+
+ __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 <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+#include <linux/irqchip/arm-gic.h>
+#include <asm/mach-types.h>
+#include <asm/localtimer.h>
+#include <asm/smp_scu.h>
+
+#include <mach/irqs.h>
+#include <mach/addr-map.h>
+
+#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);
+}
+
+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 <[email protected]>
+ * 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 <linux/kernel.h>
+#include <linux/smp.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/mach/map.h>
+
+#include <mach/addr-map.h>
+
+#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 <[email protected]>
+ * 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


2013-04-13 13:08:16

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 4/4] ARM: mmp: add SMP support for pxa988

On Thu, Apr 11, 2013 at 11:39 AM, Neil Zhang <[email protected]> wrote:
> Add SMP support for pxa988.
>
> Signed-off-by: Neil Zhang <[email protected]>
> Signed-off-by: Chao Xie <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * 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 <linux/linkage.h>
> +#include <linux/init.h>
> +#include <asm/memory.h>
> +#include <asm/cache.h>
> +#include <asm/assembler.h>
> +#include <mach/addr-map.h>
> +
> + __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 <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/smp.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +
> +#include <asm/cacheflush.h>
> +#include <mach/hardware.h>
> +#include <linux/irqchip/arm-gic.h>
> +#include <asm/mach-types.h>
> +#include <asm/localtimer.h>
> +#include <asm/smp_scu.h>
> +
> +#include <mach/irqs.h>
> +#include <mach/addr-map.h>
> +
> +#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 <[email protected]>
> + * 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 <linux/kernel.h>
> +#include <linux/smp.h>
> +
> +#include <asm/io.h>
> +#include <asm/cacheflush.h>
> +#include <asm/mach/map.h>
> +
> +#include <mach/addr-map.h>
> +
> +#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 <[email protected]>
> + * 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
>

2013-04-15 11:29:03

by Neil Zhang

[permalink] [raw]
Subject: RE: [PATCH 4/4] ARM: mmp: add SMP support for pxa988




> -----Original Message-----
> From: Haojian Zhuang [mailto:[email protected]]
> Sent: 2013??4??13?? 21:08
> To: Neil Zhang
> Cc: Grant Likely; [email protected];
> [email protected]; Chao Xie
> Subject: Re: [PATCH 4/4] ARM: mmp: add SMP support for pxa988
>
> On Thu, Apr 11, 2013 at 11:39 AM, Neil Zhang <[email protected]>
> wrote:
> > Add SMP support for pxa988.
> >
> > Signed-off-by: Neil Zhang <[email protected]>
> > Signed-off-by: Chao Xie <[email protected]>
> > ---
> > 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.

CPU_PXA988 only select HAVE_SMP, we can still not enable SMP.


>
> > 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 <[email protected]>
> > + *
> > + * 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 <linux/linkage.h>
> > +#include <linux/init.h>
> > +#include <asm/memory.h>
> > +#include <asm/cache.h>
> > +#include <asm/assembler.h>
> > +#include <mach/addr-map.h>
> > +
> > + __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 <linux/init.h>
> > +#include <linux/errno.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/jiffies.h>
> > +#include <linux/smp.h>
> > +#include <linux/io.h>
> > +#include <linux/irq.h>
> > +
> > +#include <asm/cacheflush.h>
> > +#include <mach/hardware.h>
> > +#include <linux/irqchip/arm-gic.h>
> > +#include <asm/mach-types.h>
> > +#include <asm/localtimer.h>
> > +#include <asm/smp_scu.h>
> > +
> > +#include <mach/irqs.h>
> > +#include <mach/addr-map.h>
> > +
> > +#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 <[email protected]>
> > + * 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 <linux/kernel.h>
> > +#include <linux/smp.h>
> > +
> > +#include <asm/io.h>
> > +#include <asm/cacheflush.h>
> > +#include <asm/mach/map.h>
> > +
> > +#include <mach/addr-map.h>
> > +
> > +#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 <[email protected]>
> > + * 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
> >


Best Regards,
Neil Zhang
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2013-04-19 14:09:52

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 4/4] ARM: mmp: add SMP support for pxa988

On Sat, Apr 13, 2013 at 09:08:12PM +0800, Haojian Zhuang wrote:
> On Thu, Apr 11, 2013 at 11:39 AM, Neil Zhang <[email protected]> wrote:
> > + /*
> > + * Synchronise with the boot thread.
> > + */
> > + spin_lock(&boot_lock);
> > + spin_unlock(&boot_lock);
> Lock & unlock without protecting anything. If so, you can remove this.

... which means you don't understand what is going on in this code,
and probably didn't read the comment above this fragment. The above
is to synchronise with the code below - again, read the comment at
the spin unlock in this function, remembering that the above code
and the code below runs concurrently on two different CPUs:

> > +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;
> > +}

2013-04-19 15:42:56

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 4/4] ARM: mmp: add SMP support for pxa988

On Fri, Apr 19, 2013 at 10:09 PM, Russell King - ARM Linux
<[email protected]> wrote:
> On Sat, Apr 13, 2013 at 09:08:12PM +0800, Haojian Zhuang wrote:
>> On Thu, Apr 11, 2013 at 11:39 AM, Neil Zhang <[email protected]> wrote:
>> > + /*
>> > + * Synchronise with the boot thread.
>> > + */
>> > + spin_lock(&boot_lock);
>> > + spin_unlock(&boot_lock);
>> Lock & unlock without protecting anything. If so, you can remove this.
>
> ... which means you don't understand what is going on in this code,
> and probably didn't read the comment above this fragment. The above
> is to synchronise with the code below - again, read the comment at
> the spin unlock in this function, remembering that the above code
> and the code below runs concurrently on two different CPUs:
>
Yes, I misunderstood it. Thanks for your correction.

Regards
Haojian