uboot-d1: add bootloader for upcoming d1 target
[openwrt/staging/981213.git] / package / boot / uboot-d1 / patches / 0043-sunxi-psci-Delegate-PSCI-to-SCPI.patch
1 From 650fab5c589a883b139b4164527101f9c849f1a5 Mon Sep 17 00:00:00 2001
2 From: Samuel Holland <samuel@sholland.org>
3 Date: Sat, 9 Oct 2021 23:01:05 -0500
4 Subject: [PATCH 43/90] sunxi: psci: Delegate PSCI to SCPI
5
6 This adds a new PSCI implementation which communicates with SCP firmware
7 running on the AR100 using the SCPI protocol. This allows it to support
8 the full set of PSCI v1.1 features, including CPU idle states, system
9 suspend, and multiple reset methods.
10
11 Signed-off-by: Samuel Holland <samuel@sholland.org>
12 ---
13 arch/arm/cpu/armv7/Kconfig | 1 +
14 arch/arm/cpu/armv7/sunxi/Makefile | 4 +
15 arch/arm/cpu/armv7/sunxi/psci-scpi.c | 451 +++++++++++++++++++++++++++
16 3 files changed, 456 insertions(+)
17 create mode 100644 arch/arm/cpu/armv7/sunxi/psci-scpi.c
18
19 --- a/arch/arm/cpu/armv7/Kconfig
20 +++ b/arch/arm/cpu/armv7/Kconfig
21 @@ -75,6 +75,7 @@ config ARMV7_PSCI
22 choice
23 prompt "Supported PSCI version"
24 depends on ARMV7_PSCI
25 + default ARMV7_PSCI_1_1 if MACH_SUN8I_H3
26 default ARMV7_PSCI_0_1 if ARCH_SUNXI
27 default ARMV7_PSCI_1_0
28 help
29 --- a/arch/arm/cpu/armv7/sunxi/Makefile
30 +++ b/arch/arm/cpu/armv7/sunxi/Makefile
31 @@ -13,8 +13,12 @@ obj-$(CONFIG_MACH_SUN6I) += sram.o
32 obj-$(CONFIG_MACH_SUN8I) += sram.o
33
34 ifndef CONFIG_SPL_BUILD
35 +ifdef CONFIG_MACH_SUN8I_H3
36 +obj-$(CONFIG_ARMV7_PSCI) += psci-scpi.o
37 +else
38 obj-$(CONFIG_ARMV7_PSCI) += psci.o
39 endif
40 +endif
41
42 ifdef CONFIG_SPL_BUILD
43 obj-y += fel_utils.o
44 --- /dev/null
45 +++ b/arch/arm/cpu/armv7/sunxi/psci-scpi.c
46 @@ -0,0 +1,451 @@
47 +// SPDX-License-Identifier: GPL-2.0
48 +/*
49 + * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
50 + * Copyright (C) 2018-2021 Samuel Holland <samuel@sholland.org>
51 + */
52 +
53 +#include <common.h>
54 +#include <asm/arch/cpu.h>
55 +#include <asm/arch/cpucfg.h>
56 +#include <asm/armv7.h>
57 +#include <asm/gic.h>
58 +#include <asm/io.h>
59 +#include <asm/psci.h>
60 +#include <asm/secure.h>
61 +#include <asm/system.h>
62 +
63 +#define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
64 +#define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
65 +
66 +#define HW_ON 0
67 +#define HW_OFF 1
68 +#define HW_STANDBY 2
69 +
70 +#define MPIDR_AFFLVL0(mpidr) (mpidr & 0xf)
71 +#define MPIDR_AFFLVL1(mpidr) (mpidr >> 8 & 0xf)
72 +
73 +#define SCPI_SHMEM_BASE 0x0004be00
74 +#define SCPI_SHMEM ((struct scpi_shmem *)SCPI_SHMEM_BASE)
75 +
76 +#define SCPI_RX_CHANNEL 1
77 +#define SCPI_TX_CHANNEL 0
78 +#define SCPI_VIRTUAL_CHANNEL BIT(0)
79 +
80 +#define SCPI_MESSAGE_SIZE 0x100
81 +#define SCPI_PAYLOAD_SIZE (SCPI_MESSAGE_SIZE - sizeof(struct scpi_header))
82 +
83 +#define SUNXI_MSGBOX_BASE 0x01c17000
84 +#define REMOTE_IRQ_STAT_REG (SUNXI_MSGBOX_BASE + 0x0050)
85 +#define LOCAL_IRQ_STAT_REG (SUNXI_MSGBOX_BASE + 0x0070)
86 +#define MSG_STAT_REG(n) (SUNXI_MSGBOX_BASE + 0x0140 + 0x4 * (n))
87 +#define MSG_DATA_REG(n) (SUNXI_MSGBOX_BASE + 0x0180 + 0x4 * (n))
88 +
89 +#define RX_IRQ(n) BIT(0 + 2 * (n))
90 +#define TX_IRQ(n) BIT(1 + 2 * (n))
91 +
92 +enum {
93 + CORE_POWER_LEVEL = 0,
94 + CLUSTER_POWER_LEVEL = 1,
95 + CSS_POWER_LEVEL = 2,
96 +};
97 +
98 +enum {
99 + SCPI_CMD_SCP_READY = 0x01,
100 + SCPI_CMD_SET_CSS_POWER_STATE = 0x03,
101 + SCPI_CMD_GET_CSS_POWER_STATE = 0x04,
102 + SCPI_CMD_SET_SYS_POWER_STATE = 0x05,
103 +};
104 +
105 +enum {
106 + SCPI_E_OK = 0,
107 + SCPI_E_PARAM = 1,
108 + SCPI_E_ALIGN = 2,
109 + SCPI_E_SIZE = 3,
110 + SCPI_E_HANDLER = 4,
111 + SCPI_E_ACCESS = 5,
112 + SCPI_E_RANGE = 6,
113 + SCPI_E_TIMEOUT = 7,
114 + SCPI_E_NOMEM = 8,
115 + SCPI_E_PWRSTATE = 9,
116 + SCPI_E_SUPPORT = 10,
117 + SCPI_E_DEVICE = 11,
118 + SCPI_E_BUSY = 12,
119 + SCPI_E_OS = 13,
120 + SCPI_E_DATA = 14,
121 + SCPI_E_STATE = 15,
122 +};
123 +
124 +enum {
125 + SCPI_POWER_ON = 0x00,
126 + SCPI_POWER_RETENTION = 0x01,
127 + SCPI_POWER_OFF = 0x03,
128 +};
129 +
130 +enum {
131 + SCPI_SYSTEM_SHUTDOWN = 0x00,
132 + SCPI_SYSTEM_REBOOT = 0x01,
133 + SCPI_SYSTEM_RESET = 0x02,
134 +};
135 +
136 +struct scpi_header {
137 + u8 command;
138 + u8 sender;
139 + u16 size;
140 + u32 status;
141 +};
142 +
143 +struct scpi_message {
144 + struct scpi_header header;
145 + u8 payload[SCPI_PAYLOAD_SIZE];
146 +};
147 +
148 +struct scpi_shmem {
149 + struct scpi_message rx;
150 + struct scpi_message tx;
151 +};
152 +
153 +static bool __secure_data gic_dist_init;
154 +
155 +static u32 __secure_data lock;
156 +
157 +static inline u32 __secure read_mpidr(void)
158 +{
159 + u32 val;
160 +
161 + asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (val));
162 +
163 + return val;
164 +}
165 +
166 +static void __secure scpi_begin_command(void)
167 +{
168 + u32 mpidr = read_mpidr();
169 +
170 + do {
171 + while (readl(&lock));
172 + writel(mpidr, &lock);
173 + dsb();
174 + } while (readl(&lock) != mpidr);
175 + while (readl(REMOTE_IRQ_STAT_REG) & RX_IRQ(SCPI_TX_CHANNEL));
176 +}
177 +
178 +static void __secure scpi_send_command(void)
179 +{
180 + writel(SCPI_VIRTUAL_CHANNEL, MSG_DATA_REG(SCPI_TX_CHANNEL));
181 +}
182 +
183 +static void __secure scpi_wait_response(void)
184 +{
185 + while (!readl(MSG_STAT_REG(SCPI_RX_CHANNEL)));
186 +}
187 +
188 +static void __secure scpi_end_command(void)
189 +{
190 + while (readl(MSG_STAT_REG(SCPI_RX_CHANNEL)))
191 + readl(MSG_DATA_REG(SCPI_RX_CHANNEL));
192 + writel(RX_IRQ(SCPI_RX_CHANNEL), LOCAL_IRQ_STAT_REG);
193 + writel(0, &lock);
194 +}
195 +
196 +static void __secure scpi_set_css_power_state(u32 target_cpu, u32 core_state,
197 + u32 cluster_state, u32 css_state)
198 +{
199 + struct scpi_shmem *shmem = SCPI_SHMEM;
200 +
201 + scpi_begin_command();
202 +
203 + shmem->tx.header.command = SCPI_CMD_SET_CSS_POWER_STATE;
204 + shmem->tx.header.size = 4;
205 +
206 + shmem->tx.payload[0] = target_cpu >> 4 | target_cpu;
207 + shmem->tx.payload[1] = cluster_state << 4 | core_state;
208 + shmem->tx.payload[2] = css_state;
209 + shmem->tx.payload[3] = 0;
210 +
211 + scpi_send_command();
212 + scpi_end_command();
213 +}
214 +
215 +static s32 __secure scpi_get_css_power_state(u32 target_cpu, u8 *core_states,
216 + u8 *cluster_state)
217 +{
218 + struct scpi_shmem *shmem = SCPI_SHMEM;
219 + u32 cluster = MPIDR_AFFLVL1(target_cpu);
220 + u32 offset;
221 + s32 ret;
222 +
223 + scpi_begin_command();
224 +
225 + shmem->tx.header.command = SCPI_CMD_GET_CSS_POWER_STATE;
226 + shmem->tx.header.size = 0;
227 +
228 + scpi_send_command();
229 + scpi_wait_response();
230 +
231 + for (offset = 0; offset < shmem->rx.header.size; offset += 2) {
232 + if ((shmem->rx.payload[offset] & 0xf) == cluster) {
233 + *cluster_state = shmem->rx.payload[offset+0] >> 4;
234 + *core_states = shmem->rx.payload[offset+1];
235 +
236 + break;
237 + }
238 + }
239 +
240 + ret = shmem->rx.header.status;
241 +
242 + scpi_end_command();
243 +
244 + return ret;
245 +}
246 +
247 +static s32 __secure scpi_set_sys_power_state(u32 sys_state)
248 +{
249 + struct scpi_shmem *shmem = SCPI_SHMEM;
250 + s32 ret;
251 +
252 + scpi_begin_command();
253 +
254 + shmem->tx.header.command = SCPI_CMD_SET_SYS_POWER_STATE;
255 + shmem->tx.header.size = 1;
256 +
257 + shmem->tx.payload[0] = sys_state;
258 +
259 + scpi_send_command();
260 + scpi_wait_response();
261 +
262 + ret = shmem->rx.header.status;
263 +
264 + scpi_end_command();
265 +
266 + return ret;
267 +}
268 +
269 +void psci_enable_smp(void);
270 +
271 +static s32 __secure psci_suspend_common(u32 pc, u32 context_id, u32 core_state,
272 + u32 cluster_state, u32 css_state)
273 +
274 +{
275 + u32 target_cpu = read_mpidr();
276 +
277 + if (core_state == SCPI_POWER_OFF)
278 + psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id);
279 + if (css_state == SCPI_POWER_OFF)
280 + gic_dist_init = true;
281 +
282 + scpi_set_css_power_state(target_cpu, core_state,
283 + cluster_state, css_state);
284 +
285 + psci_cpu_off_common();
286 +
287 + wfi();
288 +
289 + psci_enable_smp();
290 +
291 + return ARM_PSCI_RET_SUCCESS;
292 +}
293 +
294 +u32 __secure psci_version(void)
295 +{
296 + return ARM_PSCI_VER_1_1;
297 +}
298 +
299 +s32 __secure psci_cpu_suspend(u32 __always_unused function_id,
300 + u32 power_state, u32 pc, u32 context_id)
301 +{
302 + return psci_suspend_common(pc, context_id,
303 + power_state >> 0 & 0xf,
304 + power_state >> 4 & 0xf,
305 + power_state >> 8 & 0xf);
306 +}
307 +
308 +s32 __secure psci_cpu_off(void)
309 +{
310 + u32 pc = 0, context_id = 0;
311 +
312 + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
313 + SCPI_POWER_OFF, SCPI_POWER_ON);
314 +}
315 +
316 +s32 __secure psci_cpu_on(u32 __always_unused function_id,
317 + u32 target_cpu, u32 pc, u32 context_id)
318 +{
319 + psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id);
320 +
321 + scpi_set_css_power_state(target_cpu, SCPI_POWER_ON,
322 + SCPI_POWER_ON, SCPI_POWER_ON);
323 +
324 + return ARM_PSCI_RET_SUCCESS;
325 +}
326 +
327 +s32 __secure psci_affinity_info(u32 function_id,
328 + u32 target_cpu, u32 power_level)
329 +{
330 + if (power_level != CORE_POWER_LEVEL)
331 + return ARM_PSCI_RET_INVAL;
332 +
333 + /* This happens to have the same HW_ON/HW_OFF encoding. */
334 + return psci_node_hw_state(function_id, target_cpu, power_level);
335 +}
336 +
337 +void __secure psci_system_off(void)
338 +{
339 + scpi_set_sys_power_state(SCPI_SYSTEM_SHUTDOWN);
340 +
341 + /* Wait to be turned off. */
342 + for (;;) wfi();
343 +}
344 +
345 +void __secure psci_system_reset(void)
346 +{
347 + scpi_set_sys_power_state(SCPI_SYSTEM_REBOOT);
348 +
349 + /* Wait to be turned off. */
350 + for (;;) wfi();
351 +}
352 +
353 +s32 __secure psci_features(u32 __always_unused function_id,
354 + u32 psci_fid)
355 +{
356 + switch (psci_fid) {
357 + case ARM_PSCI_0_2_FN_PSCI_VERSION:
358 + case ARM_PSCI_0_2_FN_CPU_SUSPEND:
359 + case ARM_PSCI_0_2_FN_CPU_OFF:
360 + case ARM_PSCI_0_2_FN_CPU_ON:
361 + case ARM_PSCI_0_2_FN_AFFINITY_INFO:
362 + case ARM_PSCI_0_2_FN_SYSTEM_OFF:
363 + case ARM_PSCI_0_2_FN_SYSTEM_RESET:
364 + case ARM_PSCI_1_0_FN_PSCI_FEATURES:
365 + case ARM_PSCI_1_0_FN_CPU_DEFAULT_SUSPEND:
366 + case ARM_PSCI_1_0_FN_NODE_HW_STATE:
367 + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND:
368 + case ARM_PSCI_1_1_FN_SYSTEM_RESET2:
369 + return ARM_PSCI_RET_SUCCESS;
370 + default:
371 + return ARM_PSCI_RET_NI;
372 + }
373 +}
374 +
375 +s32 __secure psci_cpu_default_suspend(u32 __always_unused function_id,
376 + u32 pc, u32 context_id)
377 +{
378 + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
379 + SCPI_POWER_OFF, SCPI_POWER_RETENTION);
380 +}
381 +
382 +s32 __secure psci_node_hw_state(u32 __always_unused function_id,
383 + u32 target_cpu, u32 power_level)
384 +{
385 + u32 core = MPIDR_AFFLVL0(target_cpu);
386 + u8 core_states, cluster_state;
387 +
388 + if (power_level >= CSS_POWER_LEVEL)
389 + return HW_ON;
390 + if (scpi_get_css_power_state(target_cpu, &core_states, &cluster_state))
391 + return ARM_PSCI_RET_NI;
392 + if (power_level == CLUSTER_POWER_LEVEL) {
393 + if (cluster_state == SCPI_POWER_ON)
394 + return HW_ON;
395 + if (cluster_state < SCPI_POWER_OFF)
396 + return HW_STANDBY;
397 + return HW_OFF;
398 + }
399 +
400 + return (core_states & BIT(core)) ? HW_ON : HW_OFF;
401 +}
402 +
403 +s32 __secure psci_system_suspend(u32 __always_unused function_id,
404 + u32 pc, u32 context_id)
405 +{
406 + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF,
407 + SCPI_POWER_OFF, SCPI_POWER_OFF);
408 +}
409 +
410 +s32 __secure psci_system_reset2(u32 __always_unused function_id,
411 + u32 reset_type, u32 cookie)
412 +{
413 + s32 ret;
414 +
415 + if (reset_type)
416 + return ARM_PSCI_RET_INVAL;
417 +
418 + ret = scpi_set_sys_power_state(SCPI_SYSTEM_RESET);
419 + if (ret)
420 + return ARM_PSCI_RET_INVAL;
421 +
422 + /* Wait to be turned off. */
423 + for (;;) wfi();
424 +}
425 +
426 +/*
427 + * R40 is different from other single cluster SoCs. The secondary core
428 + * entry address register is in the SRAM controller address range.
429 + */
430 +#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
431 +
432 +#ifdef CONFIG_MACH_SUN8I_R40
433 +/* secondary core entry address is programmed differently on R40 */
434 +static void __secure sunxi_set_entry_address(void *entry)
435 +{
436 + writel((u32)entry,
437 + SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
438 +}
439 +#else
440 +static void __secure sunxi_set_entry_address(void *entry)
441 +{
442 + struct sunxi_cpucfg_reg *cpucfg =
443 + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
444 +
445 + writel((u32)entry, &cpucfg->priv0);
446 +
447 +#ifdef CONFIG_MACH_SUN8I_H3
448 + /* Redirect CPU 0 to the secure monitor via the resume shim. */
449 + writel(0x16aaefe8, &cpucfg->super_standy_flag);
450 + writel(0xaa16efe8, &cpucfg->super_standy_flag);
451 + writel(SUNXI_RESUME_BASE, &cpucfg->priv1);
452 +#endif
453 +}
454 +#endif
455 +
456 +void __secure psci_arch_init(void)
457 +{
458 + static bool __secure_data one_time_init = true;
459 +
460 + if (one_time_init) {
461 + /* Set secondary core power-on PC. */
462 + sunxi_set_entry_address(psci_cpu_entry);
463 +
464 + /* Wait for the SCP firmware to boot. */
465 + scpi_begin_command();
466 + scpi_wait_response();
467 + scpi_end_command();
468 +
469 + one_time_init = false;
470 + }
471 +
472 + /*
473 + * Copied from arch/arm/cpu/armv7/virt-v7.c
474 + * See also gic_resume() in arch/arm/mach-imx/mx7/psci-mx7.c
475 + */
476 + if (gic_dist_init) {
477 + u32 i, itlinesnr;
478 +
479 + /* enable the GIC distributor */
480 + writel(readl(GICD_BASE + GICD_CTLR) | 0x03, GICD_BASE + GICD_CTLR);
481 +
482 + /* TYPER[4:0] contains an encoded number of available interrupts */
483 + itlinesnr = readl(GICD_BASE + GICD_TYPER) & 0x1f;
484 +
485 + /* set all bits in the GIC group registers to one to allow access
486 + * from non-secure state. The first 32 interrupts are private per
487 + * CPU and will be set later when enabling the GIC for each core
488 + */
489 + for (i = 1; i <= itlinesnr; i++)
490 + writel((unsigned)-1, GICD_BASE + GICD_IGROUPRn + 4 * i);
491 +
492 + gic_dist_init = false;
493 + }
494 +
495 + /* Be cool with non-secure. */
496 + writel(0xff, GICC_BASE + GICC_PMR);
497 +}