diff options
author | Michal Simek <michal.simek@xilinx.com> | 2013-03-20 08:50:12 -0400 |
---|---|---|
committer | Michal Simek <michal.simek@xilinx.com> | 2013-04-04 03:24:00 -0400 |
commit | aa7eb2bb4e4a22e41bbe4612ff46e5885b13c33e (patch) | |
tree | ef3f5e0578ee98f1e95b686796a64a80807ef795 /arch/arm/mach-zynq | |
parent | 2f34e0a58f34db094257e33b461d58b578004b67 (diff) |
arm: zynq: Add smp support
Zynq is dual core Cortex A9 which starts always
at zero. Using simple trampoline ensure long jump
to secondary_startup code.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-zynq')
-rw-r--r-- | arch/arm/mach-zynq/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-zynq/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-zynq/common.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-zynq/common.h | 11 | ||||
-rw-r--r-- | arch/arm/mach-zynq/headsmp.S | 24 | ||||
-rw-r--r-- | arch/arm/mach-zynq/platsmp.c | 149 | ||||
-rw-r--r-- | arch/arm/mach-zynq/slcr.c | 29 |
7 files changed, 216 insertions, 0 deletions
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig index d70651e8b705..f4a7e630bde0 100644 --- a/arch/arm/mach-zynq/Kconfig +++ b/arch/arm/mach-zynq/Kconfig | |||
@@ -8,6 +8,7 @@ config ARCH_ZYNQ | |||
8 | select ICST | 8 | select ICST |
9 | select MIGHT_HAVE_CACHE_L2X0 | 9 | select MIGHT_HAVE_CACHE_L2X0 |
10 | select USE_OF | 10 | select USE_OF |
11 | select HAVE_SMP | ||
11 | select SPARSE_IRQ | 12 | select SPARSE_IRQ |
12 | select CADENCE_TTC_TIMER | 13 | select CADENCE_TTC_TIMER |
13 | help | 14 | help |
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile index 13ee09b563ad..b595d22134ec 100644 --- a/arch/arm/mach-zynq/Makefile +++ b/arch/arm/mach-zynq/Makefile | |||
@@ -4,3 +4,4 @@ | |||
4 | 4 | ||
5 | # Common support | 5 | # Common support |
6 | obj-y := common.o slcr.o | 6 | obj-y := common.o slcr.o |
7 | obj-$(CONFIG_SMP) += headsmp.o platsmp.o | ||
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index e1b61317b86b..5bfe7035b73d 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c | |||
@@ -104,6 +104,7 @@ static const char * const zynq_dt_match[] = { | |||
104 | }; | 104 | }; |
105 | 105 | ||
106 | MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform") | 106 | MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform") |
107 | .smp = smp_ops(zynq_smp_ops), | ||
107 | .map_io = zynq_map_io, | 108 | .map_io = zynq_map_io, |
108 | .init_irq = irqchip_init, | 109 | .init_irq = irqchip_init, |
109 | .init_machine = zynq_init_machine, | 110 | .init_machine = zynq_init_machine, |
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h index d7ec3caaa1d7..fd308f8b18d9 100644 --- a/arch/arm/mach-zynq/common.h +++ b/arch/arm/mach-zynq/common.h | |||
@@ -19,6 +19,17 @@ | |||
19 | 19 | ||
20 | extern int zynq_slcr_init(void); | 20 | extern int zynq_slcr_init(void); |
21 | extern void zynq_slcr_system_reset(void); | 21 | extern void zynq_slcr_system_reset(void); |
22 | extern void zynq_slcr_cpu_stop(int cpu); | ||
23 | extern void zynq_slcr_cpu_start(int cpu); | ||
24 | |||
25 | #ifdef CONFIG_SMP | ||
26 | extern void secondary_startup(void); | ||
27 | extern char zynq_secondary_trampoline; | ||
28 | extern char zynq_secondary_trampoline_jump; | ||
29 | extern char zynq_secondary_trampoline_end; | ||
30 | extern int __cpuinit zynq_cpun_start(u32 address, int cpu); | ||
31 | extern struct smp_operations zynq_smp_ops __initdata; | ||
32 | #endif | ||
22 | 33 | ||
23 | extern void __iomem *zynq_slcr_base; | 34 | extern void __iomem *zynq_slcr_base; |
24 | extern void __iomem *zynq_scu_base; | 35 | extern void __iomem *zynq_scu_base; |
diff --git a/arch/arm/mach-zynq/headsmp.S b/arch/arm/mach-zynq/headsmp.S new file mode 100644 index 000000000000..d183cd234a9b --- /dev/null +++ b/arch/arm/mach-zynq/headsmp.S | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de> | ||
3 | * Copyright (c) 2012-2013 Xilinx | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/linkage.h> | ||
10 | #include <linux/init.h> | ||
11 | |||
12 | __CPUINIT | ||
13 | |||
14 | ENTRY(zynq_secondary_trampoline) | ||
15 | ldr r0, [pc] | ||
16 | bx r0 | ||
17 | .globl zynq_secondary_trampoline_jump | ||
18 | zynq_secondary_trampoline_jump: | ||
19 | /* Space for jumping address */ | ||
20 | .word /* cpu 1 */ | ||
21 | .globl zynq_secondary_trampoline_end | ||
22 | zynq_secondary_trampoline_end: | ||
23 | |||
24 | ENDPROC(zynq_secondary_trampoline) | ||
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c new file mode 100644 index 000000000000..cdfd888ca783 --- /dev/null +++ b/arch/arm/mach-zynq/platsmp.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * This file contains Xilinx specific SMP code, used to start up | ||
3 | * the second processor. | ||
4 | * | ||
5 | * Copyright (C) 2011-2013 Xilinx | ||
6 | * | ||
7 | * based on linux/arch/arm/mach-realview/platsmp.c | ||
8 | * | ||
9 | * Copyright (C) 2002 ARM Ltd. | ||
10 | * | ||
11 | * This software is licensed under the terms of the GNU General Public | ||
12 | * License version 2, as published by the Free Software Foundation, and | ||
13 | * may be copied, distributed, and modified under those terms. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/export.h> | ||
22 | #include <linux/jiffies.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <asm/cacheflush.h> | ||
26 | #include <asm/smp_scu.h> | ||
27 | #include <linux/irqchip/arm-gic.h> | ||
28 | #include "common.h" | ||
29 | |||
30 | /* | ||
31 | * Store number of cores in the system | ||
32 | * Because of scu_get_core_count() must be in __init section and can't | ||
33 | * be called from zynq_cpun_start() because it is in __cpuinit section. | ||
34 | */ | ||
35 | static int ncores; | ||
36 | |||
37 | /* Secondary CPU kernel startup is a 2 step process. The primary CPU | ||
38 | * starts the secondary CPU by giving it the address of the kernel and | ||
39 | * then sending it an event to wake it up. The secondary CPU then | ||
40 | * starts the kernel and tells the primary CPU it's up and running. | ||
41 | */ | ||
42 | static void __cpuinit zynq_secondary_init(unsigned int cpu) | ||
43 | { | ||
44 | /* | ||
45 | * if any interrupts are already enabled for the primary | ||
46 | * core (e.g. timer irq), then they will not have been enabled | ||
47 | * for us: do so | ||
48 | */ | ||
49 | gic_secondary_init(0); | ||
50 | } | ||
51 | |||
52 | int __cpuinit zynq_cpun_start(u32 address, int cpu) | ||
53 | { | ||
54 | u32 trampoline_code_size = &zynq_secondary_trampoline_end - | ||
55 | &zynq_secondary_trampoline; | ||
56 | |||
57 | if (cpu > ncores) { | ||
58 | pr_warn("CPU No. is not available in the system\n"); | ||
59 | return -1; | ||
60 | } | ||
61 | |||
62 | /* MS: Expectation that SLCR are directly map and accessible */ | ||
63 | /* Not possible to jump to non aligned address */ | ||
64 | if (!(address & 3) && (!address || (address >= trampoline_code_size))) { | ||
65 | /* Store pointer to ioremap area which points to address 0x0 */ | ||
66 | static u8 __iomem *zero; | ||
67 | u32 trampoline_size = &zynq_secondary_trampoline_jump - | ||
68 | &zynq_secondary_trampoline; | ||
69 | |||
70 | zynq_slcr_cpu_stop(cpu); | ||
71 | |||
72 | if (__pa(PAGE_OFFSET)) { | ||
73 | zero = ioremap(0, trampoline_code_size); | ||
74 | if (!zero) { | ||
75 | pr_warn("BOOTUP jump vectors not accessible\n"); | ||
76 | return -1; | ||
77 | } | ||
78 | } else { | ||
79 | zero = (__force u8 __iomem *)PAGE_OFFSET; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * This is elegant way how to jump to any address | ||
84 | * 0x0: Load address at 0x8 to r0 | ||
85 | * 0x4: Jump by mov instruction | ||
86 | * 0x8: Jumping address | ||
87 | */ | ||
88 | memcpy((__force void *)zero, &zynq_secondary_trampoline, | ||
89 | trampoline_size); | ||
90 | writel(address, zero + trampoline_size); | ||
91 | |||
92 | flush_cache_all(); | ||
93 | outer_flush_range(0, trampoline_code_size); | ||
94 | smp_wmb(); | ||
95 | |||
96 | if (__pa(PAGE_OFFSET)) | ||
97 | iounmap(zero); | ||
98 | |||
99 | zynq_slcr_cpu_start(cpu); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address); | ||
105 | |||
106 | return -1; | ||
107 | } | ||
108 | EXPORT_SYMBOL(zynq_cpun_start); | ||
109 | |||
110 | static int __cpuinit zynq_boot_secondary(unsigned int cpu, | ||
111 | struct task_struct *idle) | ||
112 | { | ||
113 | return zynq_cpun_start(virt_to_phys(secondary_startup), cpu); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Initialise the CPU possible map early - this describes the CPUs | ||
118 | * which may be present or become present in the system. | ||
119 | */ | ||
120 | static void __init zynq_smp_init_cpus(void) | ||
121 | { | ||
122 | int i; | ||
123 | |||
124 | ncores = scu_get_core_count(zynq_scu_base); | ||
125 | |||
126 | for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++) | ||
127 | set_cpu_possible(i, true); | ||
128 | } | ||
129 | |||
130 | static void __init zynq_smp_prepare_cpus(unsigned int max_cpus) | ||
131 | { | ||
132 | int i; | ||
133 | |||
134 | /* | ||
135 | * Initialise the present map, which describes the set of CPUs | ||
136 | * actually populated at the present time. | ||
137 | */ | ||
138 | for (i = 0; i < max_cpus; i++) | ||
139 | set_cpu_present(i, true); | ||
140 | |||
141 | scu_enable(zynq_scu_base); | ||
142 | } | ||
143 | |||
144 | struct smp_operations zynq_smp_ops __initdata = { | ||
145 | .smp_init_cpus = zynq_smp_init_cpus, | ||
146 | .smp_prepare_cpus = zynq_smp_prepare_cpus, | ||
147 | .smp_secondary_init = zynq_secondary_init, | ||
148 | .smp_boot_secondary = zynq_boot_secondary, | ||
149 | }; | ||
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c index d58c9964e883..c70969b9c258 100644 --- a/arch/arm/mach-zynq/slcr.c +++ b/arch/arm/mach-zynq/slcr.c | |||
@@ -33,6 +33,11 @@ | |||
33 | #define SLCR_UNLOCK 0x8 /* SCLR unlock register */ | 33 | #define SLCR_UNLOCK 0x8 /* SCLR unlock register */ |
34 | 34 | ||
35 | #define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */ | 35 | #define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */ |
36 | |||
37 | #define SLCR_A9_CPU_CLKSTOP 0x10 | ||
38 | #define SLCR_A9_CPU_RST 0x1 | ||
39 | |||
40 | #define SLCR_A9_CPU_RST_CTRL 0x244 /* CPU Software Reset Control */ | ||
36 | #define SLCR_REBOOT_STATUS 0x258 /* PS Reboot Status */ | 41 | #define SLCR_REBOOT_STATUS 0x258 /* PS Reboot Status */ |
37 | 42 | ||
38 | void __iomem *zynq_slcr_base; | 43 | void __iomem *zynq_slcr_base; |
@@ -62,6 +67,30 @@ void zynq_slcr_system_reset(void) | |||
62 | } | 67 | } |
63 | 68 | ||
64 | /** | 69 | /** |
70 | * zynq_slcr_cpu_start - Start cpu | ||
71 | * @cpu: cpu number | ||
72 | */ | ||
73 | void zynq_slcr_cpu_start(int cpu) | ||
74 | { | ||
75 | /* enable CPUn */ | ||
76 | writel(SLCR_A9_CPU_CLKSTOP << cpu, | ||
77 | zynq_slcr_base + SLCR_A9_CPU_RST_CTRL); | ||
78 | /* enable CLK for CPUn */ | ||
79 | writel(0x0 << cpu, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * zynq_slcr_cpu_stop - Stop cpu | ||
84 | * @cpu: cpu number | ||
85 | */ | ||
86 | void zynq_slcr_cpu_stop(int cpu) | ||
87 | { | ||
88 | /* stop CLK and reset CPUn */ | ||
89 | writel((SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu, | ||
90 | zynq_slcr_base + SLCR_A9_CPU_RST_CTRL); | ||
91 | } | ||
92 | |||
93 | /** | ||
65 | * zynq_slcr_init | 94 | * zynq_slcr_init |
66 | * Returns 0 on success, negative errno otherwise. | 95 | * Returns 0 on success, negative errno otherwise. |
67 | * | 96 | * |