xref: /openbmc/linux/arch/arm/common/mcpm_head.S (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1d2912cb1SThomas Gleixner/* SPDX-License-Identifier: GPL-2.0-only */
2e8db288eSNicolas Pitre/*
3e8db288eSNicolas Pitre * arch/arm/common/mcpm_head.S -- kernel entry point for multi-cluster PM
4e8db288eSNicolas Pitre *
5e8db288eSNicolas Pitre * Created by:  Nicolas Pitre, March 2012
6e8db288eSNicolas Pitre * Copyright:   (C) 2012-2013  Linaro Limited
7e8db288eSNicolas Pitre *
8*e318b36eSJonathan Corbet * Refer to Documentation/arch/arm/cluster-pm-race-avoidance.rst
97fe31d28SDave Martin * for details of the synchronisation algorithms used here.
10e8db288eSNicolas Pitre */
11e8db288eSNicolas Pitre
12e8db288eSNicolas Pitre#include <linux/linkage.h>
13e8db288eSNicolas Pitre#include <asm/mcpm.h>
14519ceb9fSVictor Kamensky#include <asm/assembler.h>
15e8db288eSNicolas Pitre
161ae98561SDave Martin#include "vlock.h"
171ae98561SDave Martin
18a2faac39SNick Desaulniers.arch armv7-a
19a2faac39SNick Desaulniers
207fe31d28SDave Martin.if MCPM_SYNC_CLUSTER_CPUS
217fe31d28SDave Martin.error "cpus must be the first member of struct mcpm_sync_struct"
227fe31d28SDave Martin.endif
237fe31d28SDave Martin
24e8db288eSNicolas Pitre	.macro	pr_dbg	string
25e8db288eSNicolas Pitre#if defined(CONFIG_DEBUG_LL) && defined(DEBUG)
26e8db288eSNicolas Pitre	b	1901f
27e8db288eSNicolas Pitre1902:	.asciz	"CPU"
28e8db288eSNicolas Pitre1903:	.asciz	" cluster"
29e8db288eSNicolas Pitre1904:	.asciz	": \string"
30e8db288eSNicolas Pitre	.align
31e8db288eSNicolas Pitre1901:	adr	r0, 1902b
32e8db288eSNicolas Pitre	bl	printascii
33e8db288eSNicolas Pitre	mov	r0, r9
340305ffd7SNicolas Pitre	bl	printhex2
35e8db288eSNicolas Pitre	adr	r0, 1903b
36e8db288eSNicolas Pitre	bl	printascii
37e8db288eSNicolas Pitre	mov	r0, r10
380305ffd7SNicolas Pitre	bl	printhex2
39e8db288eSNicolas Pitre	adr	r0, 1904b
40e8db288eSNicolas Pitre	bl	printascii
41e8db288eSNicolas Pitre#endif
42e8db288eSNicolas Pitre	.endm
43e8db288eSNicolas Pitre
44e8db288eSNicolas Pitre	.arm
45e8db288eSNicolas Pitre	.align
46e8db288eSNicolas Pitre
47e8db288eSNicolas PitreENTRY(mcpm_entry_point)
48e8db288eSNicolas Pitre
49519ceb9fSVictor Kamensky ARM_BE8(setend        be)
5014327c66SRussell King THUMB(	badr	r12, 1f		)
51e8db288eSNicolas Pitre THUMB(	bx	r12		)
52e8db288eSNicolas Pitre THUMB(	.thumb			)
53e8db288eSNicolas Pitre1:
54e8db288eSNicolas Pitre	mrc	p15, 0, r0, c0, c0, 5		@ MPIDR
55e8db288eSNicolas Pitre	ubfx	r9, r0, #0, #8			@ r9 = cpu
56e8db288eSNicolas Pitre	ubfx	r10, r0, #8, #8			@ r10 = cluster
57e8db288eSNicolas Pitre	mov	r3, #MAX_CPUS_PER_CLUSTER
58e8db288eSNicolas Pitre	mla	r4, r3, r10, r9			@ r4 = canonical CPU index
59e8db288eSNicolas Pitre	cmp	r4, #(MAX_CPUS_PER_CLUSTER * MAX_NR_CLUSTERS)
60e8db288eSNicolas Pitre	blo	2f
61e8db288eSNicolas Pitre
62e8db288eSNicolas Pitre	/* We didn't expect this CPU.  Try to cheaply make it quiet. */
63e8db288eSNicolas Pitre1:	wfi
64e8db288eSNicolas Pitre	wfe
65e8db288eSNicolas Pitre	b	1b
66e8db288eSNicolas Pitre
67e8db288eSNicolas Pitre2:	pr_dbg	"kernel mcpm_entry_point\n"
68e8db288eSNicolas Pitre
69e8db288eSNicolas Pitre	/*
707fe31d28SDave Martin	 * MMU is off so we need to get to various variables in a
71e8db288eSNicolas Pitre	 * position independent way.
72e8db288eSNicolas Pitre	 */
73e8db288eSNicolas Pitre	adr	r5, 3f
74de885d14SNicolas Pitre	ldmia	r5, {r0, r6, r7, r8, r11}
75de885d14SNicolas Pitre	add	r0, r5, r0			@ r0 = mcpm_entry_early_pokes
76e8db288eSNicolas Pitre	add	r6, r5, r6			@ r6 = mcpm_entry_vectors
777fe31d28SDave Martin	ldr	r7, [r5, r7]			@ r7 = mcpm_power_up_setup_phys
787fe31d28SDave Martin	add	r8, r5, r8			@ r8 = mcpm_sync
791ae98561SDave Martin	add	r11, r5, r11			@ r11 = first_man_locks
807fe31d28SDave Martin
81de885d14SNicolas Pitre	@ Perform an early poke, if any
82de885d14SNicolas Pitre	add	r0, r0, r4, lsl #3
83de885d14SNicolas Pitre	ldmia	r0, {r0, r1}
84de885d14SNicolas Pitre	teq	r0, #0
85de885d14SNicolas Pitre	strne	r1, [r0]
86de885d14SNicolas Pitre
877fe31d28SDave Martin	mov	r0, #MCPM_SYNC_CLUSTER_SIZE
887fe31d28SDave Martin	mla	r8, r0, r10, r8			@ r8 = sync cluster base
897fe31d28SDave Martin
907fe31d28SDave Martin	@ Signal that this CPU is coming UP:
917fe31d28SDave Martin	mov	r0, #CPU_COMING_UP
927fe31d28SDave Martin	mov	r5, #MCPM_SYNC_CPU_SIZE
937fe31d28SDave Martin	mla	r5, r9, r5, r8			@ r5 = sync cpu address
947fe31d28SDave Martin	strb	r0, [r5]
957fe31d28SDave Martin
967fe31d28SDave Martin	@ At this point, the cluster cannot unexpectedly enter the GOING_DOWN
977fe31d28SDave Martin	@ state, because there is at least one active CPU (this CPU).
987fe31d28SDave Martin
991ae98561SDave Martin	mov	r0, #VLOCK_SIZE
1001ae98561SDave Martin	mla	r11, r0, r10, r11		@ r11 = cluster first man lock
1011ae98561SDave Martin	mov	r0, r11
1021ae98561SDave Martin	mov	r1, r9				@ cpu
1031ae98561SDave Martin	bl	vlock_trylock			@ implies DMB
1041ae98561SDave Martin
1051ae98561SDave Martin	cmp	r0, #0				@ failed to get the lock?
1061ae98561SDave Martin	bne	mcpm_setup_wait		@ wait for cluster setup if so
1071ae98561SDave Martin
1087fe31d28SDave Martin	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
1097fe31d28SDave Martin	cmp	r0, #CLUSTER_UP			@ cluster already up?
1107fe31d28SDave Martin	bne	mcpm_setup			@ if not, set up the cluster
1117fe31d28SDave Martin
1121ae98561SDave Martin	@ Otherwise, release the first man lock and skip setup:
1131ae98561SDave Martin	mov	r0, r11
1141ae98561SDave Martin	bl	vlock_unlock
1157fe31d28SDave Martin	b	mcpm_setup_complete
1167fe31d28SDave Martin
1177fe31d28SDave Martinmcpm_setup:
1187fe31d28SDave Martin	@ Control dependency implies strb not observable before previous ldrb.
1197fe31d28SDave Martin
1207fe31d28SDave Martin	@ Signal that the cluster is being brought up:
1217fe31d28SDave Martin	mov	r0, #INBOUND_COMING_UP
1227fe31d28SDave Martin	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
1237fe31d28SDave Martin	dmb
1247fe31d28SDave Martin
1257fe31d28SDave Martin	@ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this
1267fe31d28SDave Martin	@ point onwards will observe INBOUND_COMING_UP and abort.
1277fe31d28SDave Martin
1287fe31d28SDave Martin	@ Wait for any previously-pending cluster teardown operations to abort
1297fe31d28SDave Martin	@ or complete:
1307fe31d28SDave Martinmcpm_teardown_wait:
1317fe31d28SDave Martin	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
1327fe31d28SDave Martin	cmp	r0, #CLUSTER_GOING_DOWN
1337fe31d28SDave Martin	bne	first_man_setup
1347fe31d28SDave Martin	wfe
1357fe31d28SDave Martin	b	mcpm_teardown_wait
1367fe31d28SDave Martin
1377fe31d28SDave Martinfirst_man_setup:
1387fe31d28SDave Martin	dmb
1397fe31d28SDave Martin
1407fe31d28SDave Martin	@ If the outbound gave up before teardown started, skip cluster setup:
1417fe31d28SDave Martin
1427fe31d28SDave Martin	cmp	r0, #CLUSTER_UP
1437fe31d28SDave Martin	beq	mcpm_setup_leave
1447fe31d28SDave Martin
1457fe31d28SDave Martin	@ power_up_setup is now responsible for setting up the cluster:
1467fe31d28SDave Martin
1477fe31d28SDave Martin	cmp	r7, #0
1487fe31d28SDave Martin	mov	r0, #1		@ second (cluster) affinity level
1497fe31d28SDave Martin	blxne	r7		@ Call power_up_setup if defined
1507fe31d28SDave Martin	dmb
1517fe31d28SDave Martin
1527fe31d28SDave Martin	mov	r0, #CLUSTER_UP
1537fe31d28SDave Martin	strb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
1547fe31d28SDave Martin	dmb
1557fe31d28SDave Martin
1567fe31d28SDave Martinmcpm_setup_leave:
1577fe31d28SDave Martin	@ Leave the cluster setup critical section:
1587fe31d28SDave Martin
1597fe31d28SDave Martin	mov	r0, #INBOUND_NOT_COMING_UP
1607fe31d28SDave Martin	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
16140a5c0b4SWill Deacon	dsb	st
1627fe31d28SDave Martin	sev
1637fe31d28SDave Martin
1641ae98561SDave Martin	mov	r0, r11
1651ae98561SDave Martin	bl	vlock_unlock	@ implies DMB
1661ae98561SDave Martin	b	mcpm_setup_complete
1671ae98561SDave Martin
1681ae98561SDave Martin	@ In the contended case, non-first men wait here for cluster setup
1691ae98561SDave Martin	@ to complete:
1701ae98561SDave Martinmcpm_setup_wait:
1711ae98561SDave Martin	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
1721ae98561SDave Martin	cmp	r0, #CLUSTER_UP
1731ae98561SDave Martin	wfene
1741ae98561SDave Martin	bne	mcpm_setup_wait
1751ae98561SDave Martin	dmb
1761ae98561SDave Martin
1777fe31d28SDave Martinmcpm_setup_complete:
1787fe31d28SDave Martin	@ If a platform-specific CPU setup hook is needed, it is
1797fe31d28SDave Martin	@ called from here.
1807fe31d28SDave Martin
1817fe31d28SDave Martin	cmp	r7, #0
1827fe31d28SDave Martin	mov	r0, #0		@ first (CPU) affinity level
1837fe31d28SDave Martin	blxne	r7		@ Call power_up_setup if defined
1847fe31d28SDave Martin	dmb
1857fe31d28SDave Martin
1867fe31d28SDave Martin	@ Mark the CPU as up:
1877fe31d28SDave Martin
1887fe31d28SDave Martin	mov	r0, #CPU_UP
1897fe31d28SDave Martin	strb	r0, [r5]
1907fe31d28SDave Martin
1917fe31d28SDave Martin	@ Observability order of CPU_UP and opening of the gate does not matter.
192e8db288eSNicolas Pitre
193e8db288eSNicolas Pitremcpm_entry_gated:
194e8db288eSNicolas Pitre	ldr	r5, [r6, r4, lsl #2]		@ r5 = CPU entry vector
195e8db288eSNicolas Pitre	cmp	r5, #0
196e8db288eSNicolas Pitre	wfeeq
197e8db288eSNicolas Pitre	beq	mcpm_entry_gated
1987fe31d28SDave Martin	dmb
1997fe31d28SDave Martin
200e8db288eSNicolas Pitre	pr_dbg	"released\n"
201e8db288eSNicolas Pitre	bx	r5
202e8db288eSNicolas Pitre
203e8db288eSNicolas Pitre	.align	2
204e8db288eSNicolas Pitre
205de885d14SNicolas Pitre3:	.word	mcpm_entry_early_pokes - .
206de885d14SNicolas Pitre	.word	mcpm_entry_vectors - 3b
2077fe31d28SDave Martin	.word	mcpm_power_up_setup_phys - 3b
2087fe31d28SDave Martin	.word	mcpm_sync - 3b
2091ae98561SDave Martin	.word	first_man_locks - 3b
210e8db288eSNicolas Pitre
211e8db288eSNicolas PitreENDPROC(mcpm_entry_point)
212e8db288eSNicolas Pitre
213e8db288eSNicolas Pitre	.bss
2141ae98561SDave Martin
2151ae98561SDave Martin	.align	CACHE_WRITEBACK_ORDER
2161ae98561SDave Martin	.type	first_man_locks, #object
2171ae98561SDave Martinfirst_man_locks:
2181ae98561SDave Martin	.space	VLOCK_SIZE * MAX_NR_CLUSTERS
2191ae98561SDave Martin	.align	CACHE_WRITEBACK_ORDER
220e8db288eSNicolas Pitre
221e8db288eSNicolas Pitre	.type	mcpm_entry_vectors, #object
222e8db288eSNicolas PitreENTRY(mcpm_entry_vectors)
223e8db288eSNicolas Pitre	.space	4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
2247fe31d28SDave Martin
225de885d14SNicolas Pitre	.type	mcpm_entry_early_pokes, #object
226de885d14SNicolas PitreENTRY(mcpm_entry_early_pokes)
227de885d14SNicolas Pitre	.space	8 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
228de885d14SNicolas Pitre
2297fe31d28SDave Martin	.type	mcpm_power_up_setup_phys, #object
2307fe31d28SDave MartinENTRY(mcpm_power_up_setup_phys)
2317fe31d28SDave Martin	.space  4		@ set by mcpm_sync_init()
232