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> | |
adc548d7 | 18 | #include <linux/syscore_ops.h> |
2262d6ef | 19 | #include <linux/soc/samsung/exynos-regs-pmu.h> |
ccf55117 AK |
20 | |
21 | #include <asm/cputype.h> | |
22 | #include <asm/cp15.h> | |
23 | #include <asm/mcpm.h> | |
833b5794 | 24 | #include <asm/smp_plat.h> |
ccf55117 | 25 | |
ccf55117 AK |
26 | #include "common.h" |
27 | ||
28 | #define EXYNOS5420_CPUS_PER_CLUSTER 4 | |
29 | #define EXYNOS5420_NR_CLUSTERS 2 | |
ccf55117 | 30 | |
20fe6f98 AK |
31 | #define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN BIT(9) |
32 | #define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29) | |
33 | #define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30) | |
34 | ||
adc548d7 AK |
35 | static void __iomem *ns_sram_base_addr; |
36 | ||
ccf55117 AK |
37 | /* |
38 | * The common v7_exit_coherency_flush API could not be used because of the | |
39 | * Erratum 799270 workaround. This macro is the same as the common one (in | |
40 | * arch/arm/include/asm/cacheflush.h) except for the erratum handling. | |
41 | */ | |
42 | #define exynos_v7_exit_coherency_flush(level) \ | |
43 | asm volatile( \ | |
44 | "stmfd sp!, {fp, ip}\n\t"\ | |
45 | "mrc p15, 0, r0, c1, c0, 0 @ get SCTLR\n\t" \ | |
46 | "bic r0, r0, #"__stringify(CR_C)"\n\t" \ | |
47 | "mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \ | |
48 | "isb\n\t"\ | |
49 | "bl v7_flush_dcache_"__stringify(level)"\n\t" \ | |
ccf55117 AK |
50 | "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \ |
51 | "bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \ | |
52 | /* Dummy Load of a device register to avoid Erratum 799270 */ \ | |
53 | "ldr r4, [%0]\n\t" \ | |
54 | "and r4, r4, #0\n\t" \ | |
55 | "orr r0, r0, r4\n\t" \ | |
56 | "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR\n\t" \ | |
57 | "isb\n\t" \ | |
58 | "dsb\n\t" \ | |
59 | "ldmfd sp!, {fp, ip}" \ | |
60 | : \ | |
2e94ac42 | 61 | : "Ir" (pmu_base_addr + S5P_INFORM0) \ |
ccf55117 AK |
62 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ |
63 | "r9", "r10", "lr", "memory") | |
64 | ||
5f493ace | 65 | static int exynos_cpu_powerup(unsigned int cpu, unsigned int cluster) |
ccf55117 AK |
66 | { |
67 | unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); | |
ccf55117 AK |
68 | |
69 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
70 | if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
71 | cluster >= EXYNOS5420_NR_CLUSTERS) | |
72 | return -EINVAL; | |
73 | ||
833b5794 CP |
74 | if (!exynos_cpu_power_state(cpunr)) { |
75 | exynos_cpu_power_up(cpunr); | |
76 | ||
77 | /* | |
78 | * This assumes the cluster number of the big cores(Cortex A15) | |
79 | * is 0 and the Little cores(Cortex A7) is 1. | |
80 | * When the system was booted from the Little core, | |
81 | * they should be reset during power up cpu. | |
82 | */ | |
83 | if (cluster && | |
84 | cluster == MPIDR_AFFINITY_LEVEL(cpu_logical_map(0), 1)) { | |
85 | /* | |
86 | * Before we reset the Little cores, we should wait | |
87 | * the SPARE2 register is set to 1 because the init | |
88 | * codes of the iROM will set the register after | |
89 | * initialization. | |
90 | */ | |
91 | while (!pmu_raw_readl(S5P_PMU_SPARE2)) | |
92 | udelay(10); | |
93 | ||
94 | pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu), | |
95 | EXYNOS_SWRESET); | |
96 | } | |
97 | } | |
98 | ||
5f493ace NP |
99 | return 0; |
100 | } | |
ccf55117 | 101 | |
5f493ace NP |
102 | static int exynos_cluster_powerup(unsigned int cluster) |
103 | { | |
104 | pr_debug("%s: cluster %u\n", __func__, cluster); | |
105 | if (cluster >= EXYNOS5420_NR_CLUSTERS) | |
106 | return -EINVAL; | |
ccf55117 | 107 | |
5f493ace | 108 | exynos_cluster_power_up(cluster); |
20fe6f98 | 109 | return 0; |
ccf55117 AK |
110 | } |
111 | ||
5f493ace | 112 | static void exynos_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster) |
ccf55117 | 113 | { |
5f493ace | 114 | unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); |
ccf55117 AK |
115 | |
116 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
117 | BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
118 | cluster >= EXYNOS5420_NR_CLUSTERS); | |
5f493ace NP |
119 | exynos_cpu_power_down(cpunr); |
120 | } | |
ccf55117 | 121 | |
5f493ace NP |
122 | static void exynos_cluster_powerdown_prepare(unsigned int cluster) |
123 | { | |
124 | pr_debug("%s: cluster %u\n", __func__, cluster); | |
125 | BUG_ON(cluster >= EXYNOS5420_NR_CLUSTERS); | |
126 | exynos_cluster_power_down(cluster); | |
127 | } | |
ccf55117 | 128 | |
5f493ace NP |
129 | static void exynos_cpu_cache_disable(void) |
130 | { | |
131 | /* Disable and flush the local CPU cache. */ | |
132 | exynos_v7_exit_coherency_flush(louis); | |
133 | } | |
ccf55117 | 134 | |
5f493ace NP |
135 | static void exynos_cluster_cache_disable(void) |
136 | { | |
137 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) { | |
ccf55117 | 138 | /* |
5f493ace NP |
139 | * On the Cortex-A15 we need to disable |
140 | * L2 prefetching before flushing the cache. | |
ccf55117 | 141 | */ |
5f493ace NP |
142 | asm volatile( |
143 | "mcr p15, 1, %0, c15, c0, 3\n\t" | |
144 | "isb\n\t" | |
145 | "dsb" | |
146 | : : "r" (0x400)); | |
ccf55117 AK |
147 | } |
148 | ||
5f493ace NP |
149 | /* Flush all cache levels for this cluster. */ |
150 | exynos_v7_exit_coherency_flush(all); | |
ccf55117 | 151 | |
5f493ace NP |
152 | /* |
153 | * Disable cluster-level coherency by masking | |
154 | * incoming snoops and DVM messages: | |
155 | */ | |
156 | cci_disable_port_by_cpu(read_cpuid_mpidr()); | |
ccf55117 AK |
157 | } |
158 | ||
7c5688e7 | 159 | static int exynos_wait_for_powerdown(unsigned int cpu, unsigned int cluster) |
ccf55117 AK |
160 | { |
161 | unsigned int tries = 100; | |
162 | unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); | |
163 | ||
164 | pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); | |
165 | BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || | |
166 | cluster >= EXYNOS5420_NR_CLUSTERS); | |
167 | ||
168 | /* Wait for the core state to be OFF */ | |
169 | while (tries--) { | |
5f493ace NP |
170 | if ((exynos_cpu_power_state(cpunr) == 0)) |
171 | return 0; /* success: the CPU is halted */ | |
ccf55117 AK |
172 | |
173 | /* Otherwise, wait and retry: */ | |
174 | msleep(1); | |
175 | } | |
176 | ||
177 | return -ETIMEDOUT; /* timeout */ | |
178 | } | |
179 | ||
5f493ace | 180 | static void exynos_cpu_is_up(unsigned int cpu, unsigned int cluster) |
fc2cac41 | 181 | { |
5f493ace NP |
182 | /* especially when resuming: make sure power control is set */ |
183 | exynos_cpu_powerup(cpu, cluster); | |
fc2cac41 CK |
184 | } |
185 | ||
ccf55117 | 186 | static const struct mcpm_platform_ops exynos_power_ops = { |
5f493ace NP |
187 | .cpu_powerup = exynos_cpu_powerup, |
188 | .cluster_powerup = exynos_cluster_powerup, | |
189 | .cpu_powerdown_prepare = exynos_cpu_powerdown_prepare, | |
190 | .cluster_powerdown_prepare = exynos_cluster_powerdown_prepare, | |
191 | .cpu_cache_disable = exynos_cpu_cache_disable, | |
192 | .cluster_cache_disable = exynos_cluster_cache_disable, | |
7c5688e7 | 193 | .wait_for_powerdown = exynos_wait_for_powerdown, |
5f493ace | 194 | .cpu_is_up = exynos_cpu_is_up, |
ccf55117 AK |
195 | }; |
196 | ||
ccf55117 AK |
197 | /* |
198 | * Enable cluster-level coherency, in preparation for turning on the MMU. | |
199 | */ | |
200 | static void __naked exynos_pm_power_up_setup(unsigned int affinity_level) | |
201 | { | |
202 | asm volatile ("\n" | |
203 | "cmp r0, #1\n" | |
204 | "bxne lr\n" | |
205 | "b cci_enable_port_for_self"); | |
206 | } | |
207 | ||
f99acff1 AK |
208 | static const struct of_device_id exynos_dt_mcpm_match[] = { |
209 | { .compatible = "samsung,exynos5420" }, | |
210 | { .compatible = "samsung,exynos5800" }, | |
211 | {}, | |
212 | }; | |
213 | ||
adc548d7 AK |
214 | static void exynos_mcpm_setup_entry_point(void) |
215 | { | |
216 | /* | |
217 | * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr | |
218 | * as part of secondary_cpu_start(). Let's redirect it to the | |
219 | * mcpm_entry_point(). This is done during both secondary boot-up as | |
220 | * well as system resume. | |
221 | */ | |
222 | __raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */ | |
223 | __raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */ | |
224 | __raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8); | |
225 | } | |
226 | ||
227 | static struct syscore_ops exynos_mcpm_syscore_ops = { | |
228 | .resume = exynos_mcpm_setup_entry_point, | |
229 | }; | |
230 | ||
ccf55117 AK |
231 | static int __init exynos_mcpm_init(void) |
232 | { | |
233 | struct device_node *node; | |
20fe6f98 | 234 | unsigned int value, i; |
ccf55117 AK |
235 | int ret; |
236 | ||
f99acff1 | 237 | node = of_find_matching_node(NULL, exynos_dt_mcpm_match); |
ccf55117 AK |
238 | if (!node) |
239 | return -ENODEV; | |
240 | of_node_put(node); | |
241 | ||
242 | if (!cci_probed()) | |
243 | return -ENODEV; | |
244 | ||
245 | node = of_find_compatible_node(NULL, NULL, | |
246 | "samsung,exynos4210-sysram-ns"); | |
247 | if (!node) | |
248 | return -ENODEV; | |
249 | ||
250 | ns_sram_base_addr = of_iomap(node, 0); | |
251 | of_node_put(node); | |
252 | if (!ns_sram_base_addr) { | |
253 | pr_err("failed to map non-secure iRAM base address\n"); | |
254 | return -ENOMEM; | |
255 | } | |
256 | ||
257 | /* | |
258 | * To increase the stability of KFC reset we need to program | |
259 | * the PMU SPARE3 register | |
260 | */ | |
2e94ac42 | 261 | pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3); |
ccf55117 | 262 | |
ccf55117 AK |
263 | ret = mcpm_platform_register(&exynos_power_ops); |
264 | if (!ret) | |
265 | ret = mcpm_sync_init(exynos_pm_power_up_setup); | |
fbb04990 | 266 | if (!ret) |
5f493ace | 267 | ret = mcpm_loopback(exynos_cluster_cache_disable); /* turn on the CCI */ |
ccf55117 AK |
268 | if (ret) { |
269 | iounmap(ns_sram_base_addr); | |
270 | return ret; | |
271 | } | |
272 | ||
273 | mcpm_smp_set_ops(); | |
274 | ||
275 | pr_info("Exynos MCPM support installed\n"); | |
276 | ||
20fe6f98 AK |
277 | /* |
278 | * On Exynos5420/5800 for the A15 and A7 clusters: | |
279 | * | |
280 | * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores | |
281 | * in a cluster are turned off before turning off the cluster L2. | |
282 | * | |
283 | * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered | |
284 | * off before waking it up. | |
285 | * | |
286 | * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be | |
287 | * turned on before the first man is powered up. | |
288 | */ | |
289 | for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) { | |
2e94ac42 | 290 | value = pmu_raw_readl(EXYNOS_COMMON_OPTION(i)); |
20fe6f98 AK |
291 | value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN | |
292 | EXYNOS5420_USE_ARM_CORE_DOWN_STATE | | |
293 | EXYNOS5420_USE_L2_COMMON_UP_STATE; | |
2e94ac42 | 294 | pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i)); |
20fe6f98 AK |
295 | } |
296 | ||
adc548d7 | 297 | exynos_mcpm_setup_entry_point(); |
ccf55117 | 298 | |
adc548d7 | 299 | register_syscore_ops(&exynos_mcpm_syscore_ops); |
ccf55117 AK |
300 | |
301 | return ret; | |
302 | } | |
303 | ||
304 | early_initcall(exynos_mcpm_init); |