diff options
-rw-r--r-- | arch/arm/mach-uniphier/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-uniphier/headsmp.S | 43 | ||||
-rw-r--r-- | arch/arm/mach-uniphier/platsmp.c | 185 |
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 @@ | |||
1 | obj-y := uniphier.o | 1 | obj-y := uniphier.o |
2 | obj-$(CONFIG_SMP) += platsmp.o | 2 | obj-$(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 | |||
19 | ENTRY(uniphier_smp_trampoline) | ||
20 | ARM_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 | ||
30 | 0: wfe | ||
31 | 1: ldr r0, [r3] | ||
32 | cmp r0, r2 | ||
33 | bxeq r1 @ branch to secondary_startup | ||
34 | b 0b | ||
35 | .globl uniphier_smp_trampoline_jump | ||
36 | uniphier_smp_trampoline_jump: | ||
37 | .word 0 @ set virt_to_phys(secondary_startup) | ||
38 | .globl uniphier_smp_trampoline_poll_addr | ||
39 | uniphier_smp_trampoline_poll_addr: | ||
40 | .word 0 @ set CPU ID to be kicked to this reg | ||
41 | .globl uniphier_smp_trampoline_end | ||
42 | uniphier_smp_trampoline_end: | ||
43 | ENDPROC(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 | ||
24 | static 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 | ||
26 | static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus) | 35 | static void __iomem *uniphier_smp_rom_boot_rsv2; |
36 | static unsigned int uniphier_smp_max_cpus; | ||
37 | |||
38 | extern char uniphier_smp_trampoline; | ||
39 | extern char uniphier_smp_trampoline_jump; | ||
40 | extern char uniphier_smp_trampoline_poll_addr; | ||
41 | extern 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 | */ | ||
48 | static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE; | ||
49 | static phys_addr_t uniphier_smp_trampoline_dest; | ||
50 | |||
51 | static 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 | |||
94 | static 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 | |||
129 | static 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 | |||
140 | static 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 | |||
165 | static 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; |
57 | err: | 179 | err: |
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 | ||
63 | static int uniphier_boot_secondary(unsigned int cpu, | 185 | static 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 | ||
79 | struct smp_operations uniphier_smp_ops __initdata = { | 204 | static 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 | }; |
83 | CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp", | 208 | CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp", |
84 | &uniphier_smp_ops); | 209 | &uniphier_smp_ops); |