1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright (C) 2012 Samsung Electronics. |
4 | // Kyungmin Park <kyungmin.park@samsung.com> |
5 | // Tomasz Figa <t.figa@samsung.com> |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/io.h> |
9 | #include <linux/init.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | |
13 | #include <asm/cacheflush.h> |
14 | #include <asm/cputype.h> |
15 | #include <asm/firmware.h> |
16 | #include <asm/hardware/cache-l2x0.h> |
17 | #include <asm/suspend.h> |
18 | |
19 | #include "common.h" |
20 | #include "smc.h" |
21 | |
22 | #define EXYNOS_BOOT_ADDR 0x8 |
23 | #define EXYNOS_BOOT_FLAG 0xc |
24 | |
25 | static void exynos_save_cp15(void) |
26 | { |
27 | /* Save Power control and Diagnostic registers */ |
28 | asm ("mrc p15, 0, %0, c15, c0, 0\n" |
29 | "mrc p15, 0, %1, c15, c0, 1\n" |
30 | : "=r" (cp15_save_power), "=r" (cp15_save_diag) |
31 | : : "cc" ); |
32 | } |
33 | |
34 | static int exynos_do_idle(unsigned long mode) |
35 | { |
36 | switch (mode) { |
37 | case FW_DO_IDLE_AFTR: |
38 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) |
39 | exynos_save_cp15(); |
40 | writel_relaxed(__pa_symbol(exynos_cpu_resume_ns), |
41 | sysram_ns_base_addr + 0x24); |
42 | writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20); |
43 | if (soc_is_exynos3250()) { |
44 | flush_cache_all(); |
45 | exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE, |
46 | SMC_POWERSTATE_IDLE, arg3: 0); |
47 | exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER, |
48 | SMC_POWERSTATE_IDLE, arg3: 0); |
49 | } else |
50 | exynos_smc(SMC_CMD_CPU0AFTR, arg1: 0, arg2: 0, arg3: 0); |
51 | break; |
52 | case FW_DO_IDLE_SLEEP: |
53 | exynos_smc(SMC_CMD_SLEEP, arg1: 0, arg2: 0, arg3: 0); |
54 | } |
55 | return 0; |
56 | } |
57 | |
58 | static int exynos_cpu_boot(int cpu) |
59 | { |
60 | /* |
61 | * Exynos3250 doesn't need to send smc command for secondary CPU boot |
62 | * because Exynos3250 removes WFE in secure mode. |
63 | * |
64 | * On Exynos5 devices the call is ignored by trustzone firmware. |
65 | */ |
66 | if (!soc_is_exynos4210() && !soc_is_exynos4212() && |
67 | !soc_is_exynos4412()) |
68 | return 0; |
69 | |
70 | /* |
71 | * The second parameter of SMC_CMD_CPU1BOOT command means CPU id. |
72 | * But, Exynos4212 has only one secondary CPU so second parameter |
73 | * isn't used for informing secure firmware about CPU id. |
74 | */ |
75 | if (soc_is_exynos4212()) |
76 | cpu = 0; |
77 | |
78 | exynos_smc(SMC_CMD_CPU1BOOT, arg1: cpu, arg2: 0, arg3: 0); |
79 | return 0; |
80 | } |
81 | |
82 | static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) |
83 | { |
84 | void __iomem *boot_reg; |
85 | |
86 | if (!sysram_ns_base_addr) |
87 | return -ENODEV; |
88 | |
89 | boot_reg = sysram_ns_base_addr + 0x1c; |
90 | |
91 | /* |
92 | * Almost all Exynos-series of SoCs that run in secure mode don't need |
93 | * additional offset for every CPU, with Exynos4412 being the only |
94 | * exception. |
95 | */ |
96 | if (soc_is_exynos4412()) |
97 | boot_reg += 4 * cpu; |
98 | |
99 | writel_relaxed(boot_addr, boot_reg); |
100 | return 0; |
101 | } |
102 | |
103 | static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr) |
104 | { |
105 | void __iomem *boot_reg; |
106 | |
107 | if (!sysram_ns_base_addr) |
108 | return -ENODEV; |
109 | |
110 | boot_reg = sysram_ns_base_addr + 0x1c; |
111 | |
112 | if (soc_is_exynos4412()) |
113 | boot_reg += 4 * cpu; |
114 | |
115 | *boot_addr = readl_relaxed(boot_reg); |
116 | return 0; |
117 | } |
118 | |
119 | static int exynos_cpu_suspend(unsigned long arg) |
120 | { |
121 | flush_cache_all(); |
122 | outer_flush_all(); |
123 | |
124 | exynos_smc(SMC_CMD_SLEEP, arg1: 0, arg2: 0, arg3: 0); |
125 | |
126 | pr_info("Failed to suspend the system\n" ); |
127 | writel(val: 0, addr: sysram_ns_base_addr + EXYNOS_BOOT_FLAG); |
128 | return 1; |
129 | } |
130 | |
131 | static int exynos_suspend(void) |
132 | { |
133 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) |
134 | exynos_save_cp15(); |
135 | |
136 | writel(EXYNOS_SLEEP_MAGIC, addr: sysram_ns_base_addr + EXYNOS_BOOT_FLAG); |
137 | writel(__pa_symbol(exynos_cpu_resume_ns), |
138 | addr: sysram_ns_base_addr + EXYNOS_BOOT_ADDR); |
139 | |
140 | return cpu_suspend(0, exynos_cpu_suspend); |
141 | } |
142 | |
143 | static int exynos_resume(void) |
144 | { |
145 | writel(val: 0, addr: sysram_ns_base_addr + EXYNOS_BOOT_FLAG); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static const struct firmware_ops exynos_firmware_ops = { |
151 | .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL, |
152 | .set_cpu_boot_addr = exynos_set_cpu_boot_addr, |
153 | .get_cpu_boot_addr = exynos_get_cpu_boot_addr, |
154 | .cpu_boot = exynos_cpu_boot, |
155 | .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL, |
156 | .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL, |
157 | }; |
158 | |
159 | static void exynos_l2_write_sec(unsigned long val, unsigned reg) |
160 | { |
161 | static int l2cache_enabled; |
162 | |
163 | switch (reg) { |
164 | case L2X0_CTRL: |
165 | if (val & L2X0_CTRL_EN) { |
166 | /* |
167 | * Before the cache can be enabled, due to firmware |
168 | * design, SMC_CMD_L2X0INVALL must be called. |
169 | */ |
170 | if (!l2cache_enabled) { |
171 | exynos_smc(SMC_CMD_L2X0INVALL, arg1: 0, arg2: 0, arg3: 0); |
172 | l2cache_enabled = 1; |
173 | } |
174 | } else { |
175 | l2cache_enabled = 0; |
176 | } |
177 | exynos_smc(SMC_CMD_L2X0CTRL, arg1: val, arg2: 0, arg3: 0); |
178 | break; |
179 | |
180 | case L2X0_DEBUG_CTRL: |
181 | exynos_smc(SMC_CMD_L2X0DEBUG, arg1: val, arg2: 0, arg3: 0); |
182 | break; |
183 | |
184 | default: |
185 | WARN_ONCE(1, "%s: ignoring write to reg 0x%x\n" , __func__, reg); |
186 | } |
187 | } |
188 | |
189 | static void exynos_l2_configure(const struct l2x0_regs *regs) |
190 | { |
191 | exynos_smc(SMC_CMD_L2X0SETUP1, arg1: regs->tag_latency, arg2: regs->data_latency, |
192 | arg3: regs->prefetch_ctrl); |
193 | exynos_smc(SMC_CMD_L2X0SETUP2, arg1: regs->pwr_ctrl, arg2: regs->aux_ctrl, arg3: 0); |
194 | } |
195 | |
196 | bool __init exynos_secure_firmware_available(void) |
197 | { |
198 | struct device_node *nd; |
199 | const __be32 *addr; |
200 | |
201 | nd = of_find_compatible_node(NULL, NULL, |
202 | compat: "samsung,secure-firmware" ); |
203 | if (!nd) |
204 | return false; |
205 | |
206 | addr = of_get_address(dev: nd, index: 0, NULL, NULL); |
207 | of_node_put(node: nd); |
208 | if (!addr) { |
209 | pr_err("%s: No address specified.\n" , __func__); |
210 | return false; |
211 | } |
212 | |
213 | return true; |
214 | } |
215 | |
216 | void __init exynos_firmware_init(void) |
217 | { |
218 | if (!exynos_secure_firmware_available()) |
219 | return; |
220 | |
221 | pr_info("Running under secure firmware.\n" ); |
222 | |
223 | register_firmware_ops(&exynos_firmware_ops); |
224 | |
225 | /* |
226 | * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310), |
227 | * running under secure firmware, require certain registers of L2 |
228 | * cache controller to be written in secure mode. Here .write_sec |
229 | * callback is provided to perform necessary SMC calls. |
230 | */ |
231 | if (IS_ENABLED(CONFIG_CACHE_L2X0) && |
232 | read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { |
233 | outer_cache.write_sec = exynos_l2_write_sec; |
234 | outer_cache.configure = exynos_l2_configure; |
235 | } |
236 | } |
237 | |
238 | #define REG_CPU_STATE_ADDR (sysram_ns_base_addr + 0x28) |
239 | #define BOOT_MODE_MASK 0x1f |
240 | |
241 | void exynos_set_boot_flag(unsigned int cpu, unsigned int mode) |
242 | { |
243 | unsigned int tmp; |
244 | |
245 | tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4); |
246 | |
247 | if (mode & BOOT_MODE_MASK) |
248 | tmp &= ~BOOT_MODE_MASK; |
249 | |
250 | tmp |= mode; |
251 | writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4); |
252 | } |
253 | |
254 | void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode) |
255 | { |
256 | unsigned int tmp; |
257 | |
258 | tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4); |
259 | tmp &= ~mode; |
260 | writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4); |
261 | } |
262 | |