Commit | Line | Data |
---|---|---|
ba56a987 MY |
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 | ||
b1e4006a MY |
15 | #define pr_fmt(fmt) "uniphier: " fmt |
16 | ||
ba56a987 MY |
17 | #include <linux/init.h> |
18 | #include <linux/io.h> | |
b1e4006a MY |
19 | #include <linux/ioport.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> | |
ba56a987 MY |
26 | #include <asm/smp.h> |
27 | #include <asm/smp_scu.h> | |
28 | ||
b1e4006a MY |
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 | |
ba56a987 | 34 | |
b1e4006a MY |
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) | |
ba56a987 | 52 | { |
b1e4006a MY |
53 | size_t trmp_size; |
54 | static void __iomem *trmp_base; | |
ba56a987 | 55 | |
b1e4006a MY |
56 | if (!uniphier_cache_l2_is_enabled()) { |
57 | pr_warn("outer cache is needed for SMP, but not enabled\n"); | |
58 | return -ENODEV; | |
ba56a987 MY |
59 | } |
60 | ||
b1e4006a MY |
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 | ||
ba56a987 MY |
145 | if (scu_a9_has_base()) |
146 | scu_base_phys = scu_a9_get_base(); | |
147 | ||
148 | if (!scu_base_phys) { | |
149 | pr_err("failed to get scu base\n"); | |
b1e4006a | 150 | return -ENODEV; |
ba56a987 MY |
151 | } |
152 | ||
153 | scu_base = ioremap(scu_base_phys, SZ_128); | |
154 | if (!scu_base) { | |
b1e4006a MY |
155 | pr_err("failed to map scu base\n"); |
156 | return -ENOMEM; | |
ba56a987 MY |
157 | } |
158 | ||
159 | scu_enable(scu_base); | |
160 | iounmap(scu_base); | |
161 | ||
b1e4006a MY |
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 | ||
ba56a987 MY |
178 | return; |
179 | err: | |
180 | pr_warn("disabling SMP\n"); | |
181 | init_cpu_present(&only_cpu_0); | |
b1e4006a | 182 | uniphier_smp_unprepare_trampoline(); |
ba56a987 MY |
183 | } |
184 | ||
b1e4006a MY |
185 | static int __init uniphier_smp_boot_secondary(unsigned int cpu, |
186 | struct task_struct *idle) | |
ba56a987 | 187 | { |
b1e4006a MY |
188 | if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2)) |
189 | return -EFAULT; | |
ba56a987 | 190 | |
b1e4006a MY |
191 | writel(cpu, uniphier_smp_rom_boot_rsv2); |
192 | readl(uniphier_smp_rom_boot_rsv2); /* relax */ | |
ba56a987 | 193 | |
b1e4006a MY |
194 | asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */ |
195 | ||
196 | if (cpu == uniphier_smp_max_cpus - 1) { | |
197 | /* clean up resources if this is the last CPU */ | |
198 | uniphier_smp_unprepare_trampoline(); | |
199 | } | |
ba56a987 | 200 | |
b1e4006a | 201 | return 0; |
ba56a987 MY |
202 | } |
203 | ||
75305275 | 204 | static const struct smp_operations uniphier_smp_ops __initconst = { |
ba56a987 | 205 | .smp_prepare_cpus = uniphier_smp_prepare_cpus, |
b1e4006a | 206 | .smp_boot_secondary = uniphier_smp_boot_secondary, |
ba56a987 MY |
207 | }; |
208 | CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp", | |
209 | &uniphier_smp_ops); |