162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/arm/common/mcpm_head.S -- kernel entry point for multi-cluster PM
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Created by:  Nicolas Pitre, March 2012
662306a36Sopenharmony_ci * Copyright:   (C) 2012-2013  Linaro Limited
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Refer to Documentation/arch/arm/cluster-pm-race-avoidance.rst
962306a36Sopenharmony_ci * for details of the synchronisation algorithms used here.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/linkage.h>
1362306a36Sopenharmony_ci#include <asm/mcpm.h>
1462306a36Sopenharmony_ci#include <asm/assembler.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "vlock.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci.arch armv7-a
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci.if MCPM_SYNC_CLUSTER_CPUS
2162306a36Sopenharmony_ci.error "cpus must be the first member of struct mcpm_sync_struct"
2262306a36Sopenharmony_ci.endif
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	.macro	pr_dbg	string
2562306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_LL) && defined(DEBUG)
2662306a36Sopenharmony_ci	b	1901f
2762306a36Sopenharmony_ci1902:	.asciz	"CPU"
2862306a36Sopenharmony_ci1903:	.asciz	" cluster"
2962306a36Sopenharmony_ci1904:	.asciz	": \string"
3062306a36Sopenharmony_ci	.align
3162306a36Sopenharmony_ci1901:	adr	r0, 1902b
3262306a36Sopenharmony_ci	bl	printascii
3362306a36Sopenharmony_ci	mov	r0, r9
3462306a36Sopenharmony_ci	bl	printhex2
3562306a36Sopenharmony_ci	adr	r0, 1903b
3662306a36Sopenharmony_ci	bl	printascii
3762306a36Sopenharmony_ci	mov	r0, r10
3862306a36Sopenharmony_ci	bl	printhex2
3962306a36Sopenharmony_ci	adr	r0, 1904b
4062306a36Sopenharmony_ci	bl	printascii
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci	.endm
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	.arm
4562306a36Sopenharmony_ci	.align
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciENTRY(mcpm_entry_point)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci ARM_BE8(setend        be)
5062306a36Sopenharmony_ci THUMB(	badr	r12, 1f		)
5162306a36Sopenharmony_ci THUMB(	bx	r12		)
5262306a36Sopenharmony_ci THUMB(	.thumb			)
5362306a36Sopenharmony_ci1:
5462306a36Sopenharmony_ci	mrc	p15, 0, r0, c0, c0, 5		@ MPIDR
5562306a36Sopenharmony_ci	ubfx	r9, r0, #0, #8			@ r9 = cpu
5662306a36Sopenharmony_ci	ubfx	r10, r0, #8, #8			@ r10 = cluster
5762306a36Sopenharmony_ci	mov	r3, #MAX_CPUS_PER_CLUSTER
5862306a36Sopenharmony_ci	mla	r4, r3, r10, r9			@ r4 = canonical CPU index
5962306a36Sopenharmony_ci	cmp	r4, #(MAX_CPUS_PER_CLUSTER * MAX_NR_CLUSTERS)
6062306a36Sopenharmony_ci	blo	2f
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* We didn't expect this CPU.  Try to cheaply make it quiet. */
6362306a36Sopenharmony_ci1:	wfi
6462306a36Sopenharmony_ci	wfe
6562306a36Sopenharmony_ci	b	1b
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci2:	pr_dbg	"kernel mcpm_entry_point\n"
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/*
7062306a36Sopenharmony_ci	 * MMU is off so we need to get to various variables in a
7162306a36Sopenharmony_ci	 * position independent way.
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	adr	r5, 3f
7462306a36Sopenharmony_ci	ldmia	r5, {r0, r6, r7, r8, r11}
7562306a36Sopenharmony_ci	add	r0, r5, r0			@ r0 = mcpm_entry_early_pokes
7662306a36Sopenharmony_ci	add	r6, r5, r6			@ r6 = mcpm_entry_vectors
7762306a36Sopenharmony_ci	ldr	r7, [r5, r7]			@ r7 = mcpm_power_up_setup_phys
7862306a36Sopenharmony_ci	add	r8, r5, r8			@ r8 = mcpm_sync
7962306a36Sopenharmony_ci	add	r11, r5, r11			@ r11 = first_man_locks
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	@ Perform an early poke, if any
8262306a36Sopenharmony_ci	add	r0, r0, r4, lsl #3
8362306a36Sopenharmony_ci	ldmia	r0, {r0, r1}
8462306a36Sopenharmony_ci	teq	r0, #0
8562306a36Sopenharmony_ci	strne	r1, [r0]
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	mov	r0, #MCPM_SYNC_CLUSTER_SIZE
8862306a36Sopenharmony_ci	mla	r8, r0, r10, r8			@ r8 = sync cluster base
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	@ Signal that this CPU is coming UP:
9162306a36Sopenharmony_ci	mov	r0, #CPU_COMING_UP
9262306a36Sopenharmony_ci	mov	r5, #MCPM_SYNC_CPU_SIZE
9362306a36Sopenharmony_ci	mla	r5, r9, r5, r8			@ r5 = sync cpu address
9462306a36Sopenharmony_ci	strb	r0, [r5]
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	@ At this point, the cluster cannot unexpectedly enter the GOING_DOWN
9762306a36Sopenharmony_ci	@ state, because there is at least one active CPU (this CPU).
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	mov	r0, #VLOCK_SIZE
10062306a36Sopenharmony_ci	mla	r11, r0, r10, r11		@ r11 = cluster first man lock
10162306a36Sopenharmony_ci	mov	r0, r11
10262306a36Sopenharmony_ci	mov	r1, r9				@ cpu
10362306a36Sopenharmony_ci	bl	vlock_trylock			@ implies DMB
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	cmp	r0, #0				@ failed to get the lock?
10662306a36Sopenharmony_ci	bne	mcpm_setup_wait		@ wait for cluster setup if so
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
10962306a36Sopenharmony_ci	cmp	r0, #CLUSTER_UP			@ cluster already up?
11062306a36Sopenharmony_ci	bne	mcpm_setup			@ if not, set up the cluster
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	@ Otherwise, release the first man lock and skip setup:
11362306a36Sopenharmony_ci	mov	r0, r11
11462306a36Sopenharmony_ci	bl	vlock_unlock
11562306a36Sopenharmony_ci	b	mcpm_setup_complete
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cimcpm_setup:
11862306a36Sopenharmony_ci	@ Control dependency implies strb not observable before previous ldrb.
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	@ Signal that the cluster is being brought up:
12162306a36Sopenharmony_ci	mov	r0, #INBOUND_COMING_UP
12262306a36Sopenharmony_ci	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
12362306a36Sopenharmony_ci	dmb
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	@ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this
12662306a36Sopenharmony_ci	@ point onwards will observe INBOUND_COMING_UP and abort.
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	@ Wait for any previously-pending cluster teardown operations to abort
12962306a36Sopenharmony_ci	@ or complete:
13062306a36Sopenharmony_cimcpm_teardown_wait:
13162306a36Sopenharmony_ci	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
13262306a36Sopenharmony_ci	cmp	r0, #CLUSTER_GOING_DOWN
13362306a36Sopenharmony_ci	bne	first_man_setup
13462306a36Sopenharmony_ci	wfe
13562306a36Sopenharmony_ci	b	mcpm_teardown_wait
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cifirst_man_setup:
13862306a36Sopenharmony_ci	dmb
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	@ If the outbound gave up before teardown started, skip cluster setup:
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	cmp	r0, #CLUSTER_UP
14362306a36Sopenharmony_ci	beq	mcpm_setup_leave
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	@ power_up_setup is now responsible for setting up the cluster:
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	cmp	r7, #0
14862306a36Sopenharmony_ci	mov	r0, #1		@ second (cluster) affinity level
14962306a36Sopenharmony_ci	blxne	r7		@ Call power_up_setup if defined
15062306a36Sopenharmony_ci	dmb
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	mov	r0, #CLUSTER_UP
15362306a36Sopenharmony_ci	strb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
15462306a36Sopenharmony_ci	dmb
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cimcpm_setup_leave:
15762306a36Sopenharmony_ci	@ Leave the cluster setup critical section:
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	mov	r0, #INBOUND_NOT_COMING_UP
16062306a36Sopenharmony_ci	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
16162306a36Sopenharmony_ci	dsb	st
16262306a36Sopenharmony_ci	sev
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	mov	r0, r11
16562306a36Sopenharmony_ci	bl	vlock_unlock	@ implies DMB
16662306a36Sopenharmony_ci	b	mcpm_setup_complete
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	@ In the contended case, non-first men wait here for cluster setup
16962306a36Sopenharmony_ci	@ to complete:
17062306a36Sopenharmony_cimcpm_setup_wait:
17162306a36Sopenharmony_ci	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
17262306a36Sopenharmony_ci	cmp	r0, #CLUSTER_UP
17362306a36Sopenharmony_ci	wfene
17462306a36Sopenharmony_ci	bne	mcpm_setup_wait
17562306a36Sopenharmony_ci	dmb
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cimcpm_setup_complete:
17862306a36Sopenharmony_ci	@ If a platform-specific CPU setup hook is needed, it is
17962306a36Sopenharmony_ci	@ called from here.
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	cmp	r7, #0
18262306a36Sopenharmony_ci	mov	r0, #0		@ first (CPU) affinity level
18362306a36Sopenharmony_ci	blxne	r7		@ Call power_up_setup if defined
18462306a36Sopenharmony_ci	dmb
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	@ Mark the CPU as up:
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mov	r0, #CPU_UP
18962306a36Sopenharmony_ci	strb	r0, [r5]
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	@ Observability order of CPU_UP and opening of the gate does not matter.
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cimcpm_entry_gated:
19462306a36Sopenharmony_ci	ldr	r5, [r6, r4, lsl #2]		@ r5 = CPU entry vector
19562306a36Sopenharmony_ci	cmp	r5, #0
19662306a36Sopenharmony_ci	wfeeq
19762306a36Sopenharmony_ci	beq	mcpm_entry_gated
19862306a36Sopenharmony_ci	dmb
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	pr_dbg	"released\n"
20162306a36Sopenharmony_ci	bx	r5
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	.align	2
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci3:	.word	mcpm_entry_early_pokes - .
20662306a36Sopenharmony_ci	.word	mcpm_entry_vectors - 3b
20762306a36Sopenharmony_ci	.word	mcpm_power_up_setup_phys - 3b
20862306a36Sopenharmony_ci	.word	mcpm_sync - 3b
20962306a36Sopenharmony_ci	.word	first_man_locks - 3b
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciENDPROC(mcpm_entry_point)
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	.bss
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	.align	CACHE_WRITEBACK_ORDER
21662306a36Sopenharmony_ci	.type	first_man_locks, #object
21762306a36Sopenharmony_cifirst_man_locks:
21862306a36Sopenharmony_ci	.space	VLOCK_SIZE * MAX_NR_CLUSTERS
21962306a36Sopenharmony_ci	.align	CACHE_WRITEBACK_ORDER
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	.type	mcpm_entry_vectors, #object
22262306a36Sopenharmony_ciENTRY(mcpm_entry_vectors)
22362306a36Sopenharmony_ci	.space	4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	.type	mcpm_entry_early_pokes, #object
22662306a36Sopenharmony_ciENTRY(mcpm_entry_early_pokes)
22762306a36Sopenharmony_ci	.space	8 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	.type	mcpm_power_up_setup_phys, #object
23062306a36Sopenharmony_ciENTRY(mcpm_power_up_setup_phys)
23162306a36Sopenharmony_ci	.space  4		@ set by mcpm_sync_init()
232