aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);