[PATCH 3/3] ARM: zynq: add SMP support
Steffen Trumtrar
s.trumtrar at pengutronix.de
Sat Mar 23 08:57:30 EDT 2013
The Zynq7000 has a CortexA9 with at least 2 cores. Add support to boot them all.
To do this a trampoline is needed. At runtime the jump address and two
instructions are copied to RAM by CPU0 and then executed by CPU1.
Signed-off-by: Steffen Trumtrar <s.trumtrar at pengutronix.de>
Cc: Michal Simek <michal.simek at xilinx.com>
---
arch/arm/boot/dts/zynq-7000.dtsi | 14 +++++
arch/arm/mach-zynq/Makefile | 2 +
arch/arm/mach-zynq/common.c | 1 +
arch/arm/mach-zynq/common.h | 14 +++++
arch/arm/mach-zynq/headsmp.S | 20 +++++++
arch/arm/mach-zynq/platsmp.c | 110 +++++++++++++++++++++++++++++++++++++++
6 files changed, 161 insertions(+)
create mode 100644 arch/arm/mach-zynq/headsmp.S
create mode 100644 arch/arm/mach-zynq/platsmp.c
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index c103082..258b6c9 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -15,6 +15,20 @@
/ {
compatible = "xlnx,zynq-7000";
+ cpus {
+ cpu at 0 {
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ clocks = <&armpll 0>;
+ };
+
+ cpu at 1 {
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ clocks = <&armpll 0>;
+ };
+ };
+
amba {
compatible = "simple-bus";
#address-cells = <1>;
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 397268c..906d199 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,3 +4,5 @@
# Common support
obj-y := common.o timer.o
+AFLAGS_headsmp.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 1b9bb3d..4053962 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -117,6 +117,7 @@ static const char *xilinx_dt_match[] = {
};
MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
+ .smp = smp_ops(zynq_smp_ops),
.map_io = xilinx_map_io,
.init_irq = xilinx_irqchip_init,
.init_machine = xilinx_init_machine,
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 8b4dbba..451d386 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -19,4 +19,18 @@
void __init xttcps_timer_init(void);
+#ifdef CONFIG_SMP
+extern void secondary_startup(void);
+extern char secondary_trampoline, secondary_trampoline_end;
+#endif
+
+extern struct smp_operations __initdata zynq_smp_ops;
+extern void __iomem *slcr_base_addr;
+extern void __iomem *scu_base;
+
+#define SLCR_UNLOCK_MAGIC 0xdf0d
+
+#define SLCR_UNLOCK 0x8
+#define SLCR_A9_CPU_RST_CTRL 0x244
+
#endif
diff --git a/arch/arm/mach-zynq/headsmp.S b/arch/arm/mach-zynq/headsmp.S
new file mode 100644
index 0000000..145bbae
--- /dev/null
+++ b/arch/arm/mach-zynq/headsmp.S
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * based on mach-socfpga/headsmp.S
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+ __CPUINIT
+ .arch armv7-a
+
+ENTRY(secondary_trampoline)
+ ldr r0, [pc]
+ mov pc, r0
+
+ENTRY(secondary_trampoline_end)
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
new file mode 100644
index 0000000..1ea3d4f
--- /dev/null
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * based on
+ * linux/arch/arm/mach-zynq/platsmp.c Copyright (C) 2011 Xilinx
+ *
+ * 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/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/smp_scu.h>
+
+#include "common.h"
+
+#define A9_RST_MASK (1 << 0)
+#define A9_CLKSTOP_MASK (1 << 4)
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static void __cpuinit zynq_smp_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);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static inline void zynq_set_cpu_jump(int cpu, void *jump_addr)
+{
+ if (jump_addr) {
+ int trampoline_size = &secondary_trampoline_end - &secondary_trampoline;
+
+ writel(virt_to_phys(jump_addr), phys_to_virt(8));
+ memcpy(phys_to_virt(0), &secondary_trampoline, trampoline_size);
+
+ flush_cache_all();
+ outer_flush_all();
+ smp_wmb();
+ }
+}
+
+static void zynq_enable_cpu(int cpu, bool enable)
+{
+ writel(A9_CLKSTOP_MASK << cpu, slcr_base_addr + SLCR_A9_CPU_RST_CTRL);
+ writel(0, slcr_base_addr + SLCR_A9_CPU_RST_CTRL);
+}
+
+int __cpuinit zynq_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ writel(SLCR_UNLOCK_MAGIC, slcr_base_addr + SLCR_UNLOCK);
+ writel((A9_CLKSTOP_MASK | A9_RST_MASK) << cpu,
+ slcr_base_addr + SLCR_A9_CPU_RST_CTRL);
+
+ zynq_set_cpu_jump(cpu, secondary_startup);
+ zynq_enable_cpu(cpu, true);
+
+ return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init zynq_smp_init_cpus(void)
+{
+ unsigned int ncores, i;
+
+ ncores = scu_get_core_count(scu_base);
+
+ for (i = 0; i < ncores; ++i)
+ set_cpu_possible(i, true);
+
+ /* sanity check */
+ if (ncores > num_possible_cpus()) {
+ pr_warn("zynq: no. of cores (%d) greater than configured"
+ "maximum of %d - clipping\n", ncores, num_possible_cpus());
+ ncores = num_possible_cpus();
+ }
+}
+
+static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
+{
+ scu_enable(scu_base);
+}
+
+struct smp_operations __initdata zynq_smp_ops = {
+ .smp_init_cpus = zynq_smp_init_cpus,
+ .smp_prepare_cpus = zynq_smp_prepare_cpus,
+ .smp_secondary_init = zynq_smp_secondary_init,
+ .smp_boot_secondary = zynq_smp_boot_secondary,
+};
--
1.8.2.rc2
More information about the linux-arm-kernel
mailing list