]>
Commit | Line | Data |
---|---|---|
ccf55117 AK |
1 | /* |
2 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | |
3 | * http://www.samsung.com | |
4 | * | |
5 | * arch/arm/mach-exynos/mcpm-exynos.c | |
6 | * | |
7 | * Based on arch/arm/mach-vexpress/dcscb.c | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/arm-cci.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/of_address.h> | |
18 | ||
19 | #include <asm/cputype.h> | |
20 | #include <asm/cp15.h> | |
21 | #include <asm/mcpm.h> | |
22 | ||
23 | #include "regs-pmu.h" | |
24 | #include "common.h" | |
25 | ||
26 | #define EXYNOS5420_CPUS_PER_CLUSTER 4 | |
27 | #define EXYNOS5420_NR_CLUSTERS 2 | |
28 | #define MCPM_BOOT_ADDR_OFFSET 0x1c | |
29 | ||
30 | /* | |
31 | * The common v7_exit_coherency_flush API could not be used because of the | |
32 | * Erratum 799270 workaround. This macro is the same as the common one (in | |
33 | * arch/arm/include/asm/cacheflush.h) except for the erratum handling. | |
34 | */ | |
35 | #define exynos_v7_exit_coherency_flush(level) \ | |
36 | asm volatile( \ | |
37 | "stmfd sp!, {fp, ip}\n\t"\ | |
38 | "mrc p15, 0, r0, c1, c0, 0 @ get SCTLR\n\t" \ | |
39 | "bic r0, r0, #"__stringify(CR_C)"\n\t" \ | |
40 | "mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \ | |
41 | "isb\n\t"\ | |
42 | "bl v7_flush_dcache_"__stringify(level)"\n\t" \ | |
43 | "clrex\n\t"\ | |
44 | "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \ | |
45 | "bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \ | |
46 | /* Dummy Load of a device register to avoid Erratum 799270 */ \ | |
47 | "ldr r4, [%0]\n\t" \ | |
48 | "and r4, r4, #0\n\t" \ | |
49 | "orr r0, r0, r4\n\t" \ | |
50 | "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR\n\t" \ | |
51 | "isb\n\t" \ | |
52 | "dsb\n\t" \ | |
53 | "ldmfd sp!, {fp, ip}" \ | |
54 | : \ | |
55 | : "Ir" (S5P_INFORM0) \ | |
56 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ | |
57 | "r9", "r10", "lr", "memory") | |
58 | ||
59 | /* | |
60 | * We can't use regular spinlocks. In the switcher case, it is possible | |
61 | * for an outbound CPU to call power_down() after its inbound counterpart | |
62 | * is already live using the same logical CPU number which trips lockdep | |
63 | * debugging. | |
64 | */ | |
65 | static arch_spinlock_t exynos_mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED; | |
66 | static int | |
67 | cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS]; | |
68 | ||
69 | #define exynos_cluster_usecnt(cluster) \ | |
70 | (cpu_use_count[0][cluster] + \ | |
71 | cpu_use_count[1][cluster] + \ | |
72 | cpu_use_count[2][cluster] + \ | |
73 | cpu_use_count[3][cluster]) | |
74 | ||
75 | #define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster) | |
76 | ||
77 | static int exynos_cluster_power_control(unsigned int cluster, int enable) | |
78 | { | |
79 | unsigned int tries = 100; | |
80 | unsigned int val; | |
81 | ||
82 | if (enable) { | |
83 | exynos_cluster_power_up(cluster); | |
84 | val = S5P_CORE_LOCAL_PWR_EN; | |
85 | } else { | |
86 | exynos_cluster_power_down(cluster); | |
87 | val = 0; | |
88 | } | |
89 | ||
90 | /* Wait until cluster power control is applied */ | |
91 | while (tries--) { | |
92 | if (exynos_cluster_power_state(cluster) == val) | |
93 | return 0; | |
94 | ||
95 | cpu_relax(); | |
96 | } | |
97 | pr_debug("timed out waiting for cluster %u to power %s\n", cluster, | |
98 | enable ? "on" : "off"); | |
99 | ||
100 | return -ETIMEDOUT; | |
101 | } | |
102 | ||
103 | static int exynos_power_up(unsigned int cpu, unsigned int cluster) | |
104 | { | |
105 | unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); | |
106 | int err = 0; | |
107 | ||
108 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
109 | if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
110 | cluster >= EXYNOS5420_NR_CLUSTERS) | |
111 | return -EINVAL; | |
112 | ||
113 | /* | |
114 | * Since this is called with IRQs enabled, and no arch_spin_lock_irq | |
115 | * variant exists, we need to disable IRQs manually here. | |
116 | */ | |
117 | local_irq_disable(); | |
118 | arch_spin_lock(&exynos_mcpm_lock); | |
119 | ||
120 | cpu_use_count[cpu][cluster]++; | |
121 | if (cpu_use_count[cpu][cluster] == 1) { | |
122 | bool was_cluster_down = | |
123 | (exynos_cluster_usecnt(cluster) == 1); | |
124 | ||
125 | /* | |
126 | * Turn on the cluster (L2/COMMON) and then power on the | |
127 | * cores. | |
128 | */ | |
129 | if (was_cluster_down) | |
130 | err = exynos_cluster_power_control(cluster, 1); | |
131 | ||
132 | if (!err) | |
133 | exynos_cpu_power_up(cpunr); | |
134 | else | |
135 | exynos_cluster_power_control(cluster, 0); | |
136 | } else if (cpu_use_count[cpu][cluster] != 2) { | |
137 | /* | |
138 | * The only possible values are: | |
139 | * 0 = CPU down | |
140 | * 1 = CPU (still) up | |
141 | * 2 = CPU requested to be up before it had a chance | |
142 | * to actually make itself down. | |
143 | * Any other value is a bug. | |
144 | */ | |
145 | BUG(); | |
146 | } | |
147 | ||
148 | arch_spin_unlock(&exynos_mcpm_lock); | |
149 | local_irq_enable(); | |
150 | ||
151 | return err; | |
152 | } | |
153 | ||
154 | /* | |
155 | * NOTE: This function requires the stack data to be visible through power down | |
156 | * and can only be executed on processors like A15 and A7 that hit the cache | |
157 | * with the C bit clear in the SCTLR register. | |
158 | */ | |
159 | static void exynos_power_down(void) | |
160 | { | |
161 | unsigned int mpidr, cpu, cluster; | |
162 | bool last_man = false, skip_wfi = false; | |
163 | unsigned int cpunr; | |
164 | ||
165 | mpidr = read_cpuid_mpidr(); | |
166 | cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); | |
167 | cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); | |
168 | cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); | |
169 | ||
170 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
171 | BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
172 | cluster >= EXYNOS5420_NR_CLUSTERS); | |
173 | ||
174 | __mcpm_cpu_going_down(cpu, cluster); | |
175 | ||
176 | arch_spin_lock(&exynos_mcpm_lock); | |
177 | BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); | |
178 | cpu_use_count[cpu][cluster]--; | |
179 | if (cpu_use_count[cpu][cluster] == 0) { | |
180 | exynos_cpu_power_down(cpunr); | |
181 | ||
182 | if (exynos_cluster_unused(cluster)) | |
183 | /* TODO: Turn off the cluster here to save power. */ | |
184 | last_man = true; | |
185 | } else if (cpu_use_count[cpu][cluster] == 1) { | |
186 | /* | |
187 | * A power_up request went ahead of us. | |
188 | * Even if we do not want to shut this CPU down, | |
189 | * the caller expects a certain state as if the WFI | |
190 | * was aborted. So let's continue with cache cleaning. | |
191 | */ | |
192 | skip_wfi = true; | |
193 | } else { | |
194 | BUG(); | |
195 | } | |
196 | ||
197 | if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { | |
198 | arch_spin_unlock(&exynos_mcpm_lock); | |
199 | ||
200 | if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) { | |
201 | /* | |
202 | * On the Cortex-A15 we need to disable | |
203 | * L2 prefetching before flushing the cache. | |
204 | */ | |
205 | asm volatile( | |
206 | "mcr p15, 1, %0, c15, c0, 3\n\t" | |
207 | "isb\n\t" | |
208 | "dsb" | |
209 | : : "r" (0x400)); | |
210 | } | |
211 | ||
212 | /* Flush all cache levels for this cluster. */ | |
213 | exynos_v7_exit_coherency_flush(all); | |
214 | ||
215 | /* | |
216 | * Disable cluster-level coherency by masking | |
217 | * incoming snoops and DVM messages: | |
218 | */ | |
219 | cci_disable_port_by_cpu(mpidr); | |
220 | ||
221 | __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); | |
222 | } else { | |
223 | arch_spin_unlock(&exynos_mcpm_lock); | |
224 | ||
225 | /* Disable and flush the local CPU cache. */ | |
226 | exynos_v7_exit_coherency_flush(louis); | |
227 | } | |
228 | ||
229 | __mcpm_cpu_down(cpu, cluster); | |
230 | ||
231 | /* Now we are prepared for power-down, do it: */ | |
232 | if (!skip_wfi) | |
233 | wfi(); | |
234 | ||
235 | /* Not dead at this point? Let our caller cope. */ | |
236 | } | |
237 | ||
238 | static int exynos_power_down_finish(unsigned int cpu, unsigned int cluster) | |
239 | { | |
240 | unsigned int tries = 100; | |
241 | unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); | |
242 | ||
243 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
244 | BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
245 | cluster >= EXYNOS5420_NR_CLUSTERS); | |
246 | ||
247 | /* Wait for the core state to be OFF */ | |
248 | while (tries--) { | |
249 | if (ACCESS_ONCE(cpu_use_count[cpu][cluster]) == 0) { | |
250 | if ((exynos_cpu_power_state(cpunr) == 0)) | |
251 | return 0; /* success: the CPU is halted */ | |
252 | } | |
253 | ||
254 | /* Otherwise, wait and retry: */ | |
255 | msleep(1); | |
256 | } | |
257 | ||
258 | return -ETIMEDOUT; /* timeout */ | |
259 | } | |
260 | ||
261 | static const struct mcpm_platform_ops exynos_power_ops = { | |
262 | .power_up = exynos_power_up, | |
263 | .power_down = exynos_power_down, | |
264 | .power_down_finish = exynos_power_down_finish, | |
265 | }; | |
266 | ||
267 | static void __init exynos_mcpm_usage_count_init(void) | |
268 | { | |
269 | unsigned int mpidr, cpu, cluster; | |
270 | ||
271 | mpidr = read_cpuid_mpidr(); | |
272 | cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); | |
273 | cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); | |
274 | ||
275 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
276 | BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
277 | cluster >= EXYNOS5420_NR_CLUSTERS); | |
278 | ||
279 | cpu_use_count[cpu][cluster] = 1; | |
280 | } | |
281 | ||
282 | /* | |
283 | * Enable cluster-level coherency, in preparation for turning on the MMU. | |
284 | */ | |
285 | static void __naked exynos_pm_power_up_setup(unsigned int affinity_level) | |
286 | { | |
287 | asm volatile ("\n" | |
288 | "cmp r0, #1\n" | |
289 | "bxne lr\n" | |
290 | "b cci_enable_port_for_self"); | |
291 | } | |
292 | ||
293 | static int __init exynos_mcpm_init(void) | |
294 | { | |
295 | struct device_node *node; | |
296 | void __iomem *ns_sram_base_addr; | |
297 | int ret; | |
298 | ||
299 | node = of_find_compatible_node(NULL, NULL, "samsung,exynos5420"); | |
300 | if (!node) | |
301 | return -ENODEV; | |
302 | of_node_put(node); | |
303 | ||
304 | if (!cci_probed()) | |
305 | return -ENODEV; | |
306 | ||
307 | node = of_find_compatible_node(NULL, NULL, | |
308 | "samsung,exynos4210-sysram-ns"); | |
309 | if (!node) | |
310 | return -ENODEV; | |
311 | ||
312 | ns_sram_base_addr = of_iomap(node, 0); | |
313 | of_node_put(node); | |
314 | if (!ns_sram_base_addr) { | |
315 | pr_err("failed to map non-secure iRAM base address\n"); | |
316 | return -ENOMEM; | |
317 | } | |
318 | ||
319 | /* | |
320 | * To increase the stability of KFC reset we need to program | |
321 | * the PMU SPARE3 register | |
322 | */ | |
323 | __raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3); | |
324 | ||
325 | exynos_mcpm_usage_count_init(); | |
326 | ||
327 | ret = mcpm_platform_register(&exynos_power_ops); | |
328 | if (!ret) | |
329 | ret = mcpm_sync_init(exynos_pm_power_up_setup); | |
330 | if (ret) { | |
331 | iounmap(ns_sram_base_addr); | |
332 | return ret; | |
333 | } | |
334 | ||
335 | mcpm_smp_set_ops(); | |
336 | ||
337 | pr_info("Exynos MCPM support installed\n"); | |
338 | ||
339 | /* | |
340 | * Future entries into the kernel can now go | |
341 | * through the cluster entry vectors. | |
342 | */ | |
343 | __raw_writel(virt_to_phys(mcpm_entry_point), | |
344 | ns_sram_base_addr + MCPM_BOOT_ADDR_OFFSET); | |
345 | ||
346 | iounmap(ns_sram_base_addr); | |
347 | ||
348 | return ret; | |
349 | } | |
350 | ||
351 | early_initcall(exynos_mcpm_init); |