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