aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2015-10-02 00:42:20 -0400
committerOlof Johansson <olof@lixom.net>2015-10-26 20:20:53 -0400
commitb1e4006aeda8c8784029de17d47987c21ea75f6d (patch)
tree405cd7d7b0e9f2e29c6704b828730107706beb51
parente7ecbc057bc5cffb8ad10b6bf7a80684fd426d23 (diff)
ARM: uniphier: rework SMP operations to use trampoline code
The complexity of the boot sequence of UniPhier SoC family is a PITA due to the following hardware limitations: [1] No dedicated on-chip SRAM SoCs in general have small SRAM, on which a tiny firmware or a boot loader can run before SDRAM is initialized. As UniPhier SoCs do not have any dedicated SRAM accessible from CPUs, the locked outer cache is used instead. Due to the ARM specification, to have access to the outer cache, the MMU must be enabled. This is done for all CPU cores by the program hard-wired in the boot ROM. The boot ROM code loads a small amount of program (this is usually SPL of U-Boot) from a non-volatile device onto the locked outer cache, and the primary CPU jumps to it. The secondary CPUs stay in the boot ROM until they are kicked by the primary CPU. [2] CPUs can not directly jump to SDRAM address space As mentioned above, the MMU is enable for all the CPUs with the page table hard-wired in the boot ROM. Unfortunately, the page table only has minimal sets of valid sections; all the sections of SDRAM address space are zero-filled. That means all the CPUs, including secondary ones, can not jump directly to SDRAM address space. So, the primary CPU must bring up secondary CPUs to accessible address mapped onto the outer cache, then again kick them to SDRAM address space. Before this commit, this complex task was done with help of a boot loader (U-Boot); U-Boot SPL brings up the secondary CPUs to the entry of U-Boot SPL and they stay there until they are kicked by Linux. This is not nice because a boot loader must put the secondary CPUs into a certain state expected by the kernel. It makes difficult to port another boot loader because the boot loader and the kernel must work in sync to wake up the secondary CPUs. This commit reworks the SMP operations so that they do not rely on particular boot loader implementation; the SMP operations (platsmp.c) put trampoline code (headsmp.S) on a locked way of the outer cache. The secondary CPUs jump from the boot ROM to secondary_entry via the trampoline code. The boot loader no longer needs to take care of SMP. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r--arch/arm/mach-uniphier/Makefile2
-rw-r--r--arch/arm/mach-uniphier/headsmp.S43
-rw-r--r--arch/arm/mach-uniphier/platsmp.c185
3 files changed, 199 insertions, 31 deletions
diff --git a/arch/arm/mach-uniphier/Makefile b/arch/arm/mach-uniphier/Makefile
index 60bd2265f753..1233f9b610bc 100644
--- a/arch/arm/mach-uniphier/Makefile
+++ b/arch/arm/mach-uniphier/Makefile
@@ -1,2 +1,2 @@
1obj-y := uniphier.o 1obj-y := uniphier.o
2obj-$(CONFIG_SMP) += platsmp.o 2obj-$(CONFIG_SMP) += platsmp.o headsmp.o
diff --git a/arch/arm/mach-uniphier/headsmp.S b/arch/arm/mach-uniphier/headsmp.S
new file mode 100644
index 000000000000..c819dff84546
--- /dev/null
+++ b/arch/arm/mach-uniphier/headsmp.S
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/linkage.h>
16#include <asm/assembler.h>
17#include <asm/cp15.h>
18
19ENTRY(uniphier_smp_trampoline)
20ARM_BE8(setend be) @ ensure we are in BE8 mode
21 mrc p15, 0, r0, c0, c0, 5 @ MPIDR (Multiprocessor Affinity Reg)
22 and r2, r0, #0x3 @ CPU ID
23 ldr r1, uniphier_smp_trampoline_jump
24 ldr r3, uniphier_smp_trampoline_poll_addr
25 mrc p15, 0, r0, c1, c0, 0 @ SCTLR (System Control Register)
26 orr r0, r0, #CR_I @ Enable ICache
27 bic r0, r0, #(CR_C | CR_M) @ Disable MMU and Dcache
28 mcr p15, 0, r0, c1, c0, 0
29 b 1f @ cache the following 5 instructions
300: wfe
311: ldr r0, [r3]
32 cmp r0, r2
33 bxeq r1 @ branch to secondary_startup
34 b 0b
35 .globl uniphier_smp_trampoline_jump
36uniphier_smp_trampoline_jump:
37 .word 0 @ set virt_to_phys(secondary_startup)
38 .globl uniphier_smp_trampoline_poll_addr
39uniphier_smp_trampoline_poll_addr:
40 .word 0 @ set CPU ID to be kicked to this reg
41 .globl uniphier_smp_trampoline_end
42uniphier_smp_trampoline_end:
43ENDPROC(uniphier_smp_trampoline)
diff --git a/arch/arm/mach-uniphier/platsmp.c b/arch/arm/mach-uniphier/platsmp.c
index 4b784f721135..f0577664611c 100644
--- a/arch/arm/mach-uniphier/platsmp.c
+++ b/arch/arm/mach-uniphier/platsmp.c
@@ -12,73 +12,198 @@
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 */ 13 */
14 14
15#include <linux/sizes.h> 15#define pr_fmt(fmt) "uniphier: " fmt
16#include <linux/compiler.h> 16
17#include <linux/init.h> 17#include <linux/init.h>
18#include <linux/io.h> 18#include <linux/io.h>
19#include <linux/regmap.h> 19#include <linux/ioport.h>
20#include <linux/mfd/syscon.h> 20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/sizes.h>
23#include <asm/cacheflush.h>
24#include <asm/hardware/cache-uniphier.h>
25#include <asm/pgtable.h>
21#include <asm/smp.h> 26#include <asm/smp.h>
22#include <asm/smp_scu.h> 27#include <asm/smp_scu.h>
23 28
24static struct regmap *sbcm_regmap; 29/*
30 * The secondary CPUs check this register from the boot ROM for the jump
31 * destination. After that, it can be reused as a scratch register.
32 */
33#define UNIPHIER_SBC_ROM_BOOT_RSV2 0x1208
25 34
26static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus) 35static void __iomem *uniphier_smp_rom_boot_rsv2;
36static unsigned int uniphier_smp_max_cpus;
37
38extern char uniphier_smp_trampoline;
39extern char uniphier_smp_trampoline_jump;
40extern char uniphier_smp_trampoline_poll_addr;
41extern char uniphier_smp_trampoline_end;
42
43/*
44 * Copy trampoline code to the tail of the 1st section of the page table used
45 * in the boot ROM. This area is directly accessible by the secondary CPUs
46 * for all the UniPhier SoCs.
47 */
48static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE;
49static phys_addr_t uniphier_smp_trampoline_dest;
50
51static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr)
27{ 52{
28 static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 }; 53 size_t trmp_size;
29 unsigned long scu_base_phys = 0; 54 static void __iomem *trmp_base;
30 void __iomem *scu_base;
31 55
32 sbcm_regmap = syscon_regmap_lookup_by_compatible( 56 if (!uniphier_cache_l2_is_enabled()) {
33 "socionext,uniphier-system-bus-controller-misc"); 57 pr_warn("outer cache is needed for SMP, but not enabled\n");
34 if (IS_ERR(sbcm_regmap)) { 58 return -ENODEV;
35 pr_err("failed to regmap system-bus-controller-misc\n");
36 goto err;
37 } 59 }
38 60
61 uniphier_cache_l2_set_locked_ways(1);
62
63 outer_flush_all();
64
65 trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline;
66 uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end -
67 trmp_size;
68
69 uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest,
70 uniphier_smp_trampoline_dest_end);
71
72 trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size);
73 if (!trmp_base) {
74 pr_err("failed to map trampoline destination area\n");
75 return -ENOMEM;
76 }
77
78 memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size);
79
80 writel(virt_to_phys(secondary_startup),
81 trmp_base + (&uniphier_smp_trampoline_jump -
82 &uniphier_smp_trampoline));
83
84 writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr -
85 &uniphier_smp_trampoline));
86
87 flush_cache_all(); /* flush out trampoline code to outer cache */
88
89 iounmap(trmp_base);
90
91 return 0;
92}
93
94static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus)
95{
96 struct device_node *np;
97 struct resource res;
98 phys_addr_t rom_rsv2_phys;
99 int ret;
100
101 np = of_find_compatible_node(NULL, NULL,
102 "socionext,uniphier-system-bus-controller");
103 ret = of_address_to_resource(np, 1, &res);
104 if (ret) {
105 pr_err("failed to get resource of system-bus-controller\n");
106 return ret;
107 }
108
109 rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2;
110
111 ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
112 if (ret)
113 return ret;
114
115 uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4));
116 if (!uniphier_smp_rom_boot_rsv2) {
117 pr_err("failed to map ROM_BOOT_RSV2 register\n");
118 return -ENOMEM;
119 }
120
121 writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
122 asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
123
124 uniphier_smp_max_cpus = max_cpus; /* save for later use */
125
126 return 0;
127}
128
129static void __init uniphier_smp_unprepare_trampoline(void)
130{
131 iounmap(uniphier_smp_rom_boot_rsv2);
132
133 if (uniphier_smp_trampoline_dest)
134 outer_inv_range(uniphier_smp_trampoline_dest,
135 uniphier_smp_trampoline_dest_end);
136
137 uniphier_cache_l2_set_locked_ways(0);
138}
139
140static int __init uniphier_smp_enable_scu(void)
141{
142 unsigned long scu_base_phys = 0;
143 void __iomem *scu_base;
144
39 if (scu_a9_has_base()) 145 if (scu_a9_has_base())
40 scu_base_phys = scu_a9_get_base(); 146 scu_base_phys = scu_a9_get_base();
41 147
42 if (!scu_base_phys) { 148 if (!scu_base_phys) {
43 pr_err("failed to get scu base\n"); 149 pr_err("failed to get scu base\n");
44 goto err; 150 return -ENODEV;
45 } 151 }
46 152
47 scu_base = ioremap(scu_base_phys, SZ_128); 153 scu_base = ioremap(scu_base_phys, SZ_128);
48 if (!scu_base) { 154 if (!scu_base) {
49 pr_err("failed to remap scu base (0x%08lx)\n", scu_base_phys); 155 pr_err("failed to map scu base\n");
50 goto err; 156 return -ENOMEM;
51 } 157 }
52 158
53 scu_enable(scu_base); 159 scu_enable(scu_base);
54 iounmap(scu_base); 160 iounmap(scu_base);
55 161
162 return 0;
163}
164
165static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
166{
167 static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
168 int ret;
169
170 ret = uniphier_smp_prepare_trampoline(max_cpus);
171 if (ret)
172 goto err;
173
174 ret = uniphier_smp_enable_scu();
175 if (ret)
176 goto err;
177
56 return; 178 return;
57err: 179err:
58 pr_warn("disabling SMP\n"); 180 pr_warn("disabling SMP\n");
59 init_cpu_present(&only_cpu_0); 181 init_cpu_present(&only_cpu_0);
60 sbcm_regmap = NULL; 182 uniphier_smp_unprepare_trampoline();
61} 183}
62 184
63static int uniphier_boot_secondary(unsigned int cpu, 185static int __init uniphier_smp_boot_secondary(unsigned int cpu,
64 struct task_struct *idle) 186 struct task_struct *idle)
65{ 187{
66 int ret; 188 if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
189 return -EFAULT;
67 190
68 if (!sbcm_regmap) 191 writel(cpu, uniphier_smp_rom_boot_rsv2);
69 return -ENODEV; 192 readl(uniphier_smp_rom_boot_rsv2); /* relax */
70 193
71 ret = regmap_write(sbcm_regmap, 0x1208, 194 asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
72 virt_to_phys(secondary_startup)); 195
73 if (!ret) 196 if (cpu == uniphier_smp_max_cpus - 1) {
74 asm("sev"); /* wake up secondary CPU */ 197 /* clean up resources if this is the last CPU */
198 uniphier_smp_unprepare_trampoline();
199 }
75 200
76 return ret; 201 return 0;
77} 202}
78 203
79struct smp_operations uniphier_smp_ops __initdata = { 204static struct smp_operations uniphier_smp_ops __initdata = {
80 .smp_prepare_cpus = uniphier_smp_prepare_cpus, 205 .smp_prepare_cpus = uniphier_smp_prepare_cpus,
81 .smp_boot_secondary = uniphier_boot_secondary, 206 .smp_boot_secondary = uniphier_smp_boot_secondary,
82}; 207};
83CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp", 208CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
84 &uniphier_smp_ops); 209 &uniphier_smp_ops);