1/*
2 * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch.h>
8#include <asm_macros.S>
9#include <drivers/arm/gicv2.h>
10#include <drivers/arm/gicv3.h>
11#include <drivers/arm/fvp/fvp_pwrc.h>
12#include <platform_def.h>
13
14	.globl	plat_secondary_cold_boot_setup
15	.globl	plat_get_my_entrypoint
16	.globl	plat_is_my_cpu_primary
17	.globl	plat_arm_calc_core_pos
18
19	/* -----------------------------------------------------
20	 * void plat_secondary_cold_boot_setup (void);
21	 *
22	 * This function performs any platform specific actions
23	 * needed for a secondary cpu after a cold reset e.g
24	 * mark the cpu's presence, mechanism to place it in a
25	 * holding pen etc.
26	 * TODO: Should we read the PSYS register to make sure
27	 * that the request has gone through.
28	 * -----------------------------------------------------
29	 */
30func plat_secondary_cold_boot_setup
31#ifndef EL3_PAYLOAD_BASE
32	/* ---------------------------------------------
33	 * Power down this cpu.
34	 * TODO: Do we need to worry about powering the
35	 * cluster down as well here. That will need
36	 * locks which we won't have unless an elf-
37	 * loader zeroes out the zi section.
38	 * ---------------------------------------------
39	 */
40	mrs	x0, mpidr_el1
41	mov_imm	x1, PWRC_BASE
42	str	w0, [x1, #PPOFFR_OFF]
43
44	/* ---------------------------------------------
45	 * There is no sane reason to come out of this
46	 * wfi so panic if we do. This cpu will be pow-
47	 * ered on and reset by the cpu_on pm api
48	 * ---------------------------------------------
49	 */
50	dsb	sy
51	wfi
52	no_ret	plat_panic_handler
53#else
54	mov_imm	x0, PLAT_ARM_TRUSTED_MAILBOX_BASE
55
56	/* Wait until the entrypoint gets populated */
57poll_mailbox:
58	ldr	x1, [x0]
59	cbz	x1, 1f
60	br	x1
611:
62	wfe
63	b	poll_mailbox
64#endif /* EL3_PAYLOAD_BASE */
65endfunc plat_secondary_cold_boot_setup
66
67	/* ---------------------------------------------------------------------
68	 * uintptr_t plat_get_my_entrypoint (void);
69	 *
70	 * Main job of this routine is to distinguish between a cold and warm
71	 * boot. On FVP, this information can be queried from the power
72	 * controller. The Power Control SYS Status Register (PSYSR) indicates
73	 * the wake-up reason for the CPU.
74	 *
75	 * For a cold boot, return 0.
76	 * For a warm boot, read the mailbox and return the address it contains.
77	 *
78	 * TODO: PSYSR is a common register and should be
79	 * 	accessed using locks. Since it is not possible
80	 * 	to use locks immediately after a cold reset
81	 * 	we are relying on the fact that after a cold
82	 * 	reset all cpus will read the same WK field
83	 * ---------------------------------------------------------------------
84	 */
85func plat_get_my_entrypoint
86	/* ---------------------------------------------------------------------
87	 * When bit PSYSR.WK indicates either "Wake by PPONR" or "Wake by GIC
88	 * WakeRequest signal" then it is a warm boot.
89	 * ---------------------------------------------------------------------
90	 */
91	mrs	x2, mpidr_el1
92	mov_imm	x1, PWRC_BASE
93	str	w2, [x1, #PSYSR_OFF]
94	ldr	w2, [x1, #PSYSR_OFF]
95	ubfx	w2, w2, #PSYSR_WK_SHIFT, #PSYSR_WK_WIDTH
96	cmp	w2, #WKUP_PPONR
97	beq	warm_reset
98	cmp	w2, #WKUP_GICREQ
99	beq	warm_reset
100
101	/* Cold reset */
102	mov	x0, #0
103	ret
104
105warm_reset:
106	/* ---------------------------------------------------------------------
107	 * A mailbox is maintained in the trusted SRAM. It is flushed out of the
108	 * caches after every update using normal memory so it is safe to read
109	 * it here with SO attributes.
110	 * ---------------------------------------------------------------------
111	 */
112	mov_imm	x0, PLAT_ARM_TRUSTED_MAILBOX_BASE
113	ldr	x0, [x0]
114	cbz	x0, _panic_handler
115	ret
116
117	/* ---------------------------------------------------------------------
118	 * The power controller indicates this is a warm reset but the mailbox
119	 * is empty. This should never happen!
120	 * ---------------------------------------------------------------------
121	 */
122_panic_handler:
123	no_ret	plat_panic_handler
124endfunc plat_get_my_entrypoint
125
126	/* -----------------------------------------------------
127	 * unsigned int plat_is_my_cpu_primary (void);
128	 *
129	 * Find out whether the current cpu is the primary
130	 * cpu.
131	 * -----------------------------------------------------
132	 */
133func plat_is_my_cpu_primary
134	mrs	x0, mpidr_el1
135	mov_imm	x1, MPIDR_AFFINITY_MASK
136	and	x0, x0, x1
137	cmp	x0, #FVP_PRIMARY_CPU
138	cset	w0, eq
139	ret
140endfunc plat_is_my_cpu_primary
141
142	/* ---------------------------------------------------------------------
143	 * unsigned int plat_arm_calc_core_pos(u_register_t mpidr)
144	 *
145	 * Function to calculate the core position on FVP.
146	 *
147	 * (ClusterId * FVP_MAX_CPUS_PER_CLUSTER * FVP_MAX_PE_PER_CPU) +
148	 * (CPUId * FVP_MAX_PE_PER_CPU) +
149	 * ThreadId
150	 *
151	 * which can be simplified as:
152	 *
153	 * ((ClusterId * FVP_MAX_CPUS_PER_CLUSTER + CPUId) * FVP_MAX_PE_PER_CPU)
154	 * + ThreadId
155	 * ---------------------------------------------------------------------
156	 */
157func plat_arm_calc_core_pos
158	/*
159	 * Check for MT bit in MPIDR. If not set, shift MPIDR to left to make it
160	 * look as if in a multi-threaded implementation.
161	 */
162	tst	x0, #MPIDR_MT_MASK
163	lsl	x3, x0, #MPIDR_AFFINITY_BITS
164	csel	x3, x3, x0, eq
165
166	/* Extract individual affinity fields from MPIDR */
167	ubfx	x0, x3, #MPIDR_AFF0_SHIFT, #MPIDR_AFFINITY_BITS
168	ubfx	x1, x3, #MPIDR_AFF1_SHIFT, #MPIDR_AFFINITY_BITS
169	ubfx	x2, x3, #MPIDR_AFF2_SHIFT, #MPIDR_AFFINITY_BITS
170
171	/* Compute linear position */
172	mov	x4, #FVP_MAX_CPUS_PER_CLUSTER
173	madd	x1, x2, x4, x1
174	mov	x5, #FVP_MAX_PE_PER_CPU
175	madd	x0, x1, x5, x0
176	ret
177endfunc plat_arm_calc_core_pos
178