Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/common/mcpm_head.S
26292 views
1
/* SPDX-License-Identifier: GPL-2.0-only */
2
/*
3
* arch/arm/common/mcpm_head.S -- kernel entry point for multi-cluster PM
4
*
5
* Created by: Nicolas Pitre, March 2012
6
* Copyright: (C) 2012-2013 Linaro Limited
7
*
8
* Refer to Documentation/arch/arm/cluster-pm-race-avoidance.rst
9
* for details of the synchronisation algorithms used here.
10
*/
11
12
#include <linux/linkage.h>
13
#include <asm/mcpm.h>
14
#include <asm/assembler.h>
15
16
#include "vlock.h"
17
18
.arch armv7-a
19
20
.if MCPM_SYNC_CLUSTER_CPUS
21
.error "cpus must be the first member of struct mcpm_sync_struct"
22
.endif
23
24
.macro pr_dbg string
25
#if defined(CONFIG_DEBUG_LL) && defined(DEBUG)
26
b 1901f
27
1902: .asciz "CPU"
28
1903: .asciz " cluster"
29
1904: .asciz ": \string"
30
.align
31
1901: adr r0, 1902b
32
bl printascii
33
mov r0, r9
34
bl printhex2
35
adr r0, 1903b
36
bl printascii
37
mov r0, r10
38
bl printhex2
39
adr r0, 1904b
40
bl printascii
41
#endif
42
.endm
43
44
.arm
45
.align
46
47
ENTRY(mcpm_entry_point)
48
49
ARM_BE8(setend be)
50
THUMB( badr r12, 1f )
51
THUMB( bx r12 )
52
THUMB( .thumb )
53
1:
54
mrc p15, 0, r0, c0, c0, 5 @ MPIDR
55
ubfx r9, r0, #0, #8 @ r9 = cpu
56
ubfx r10, r0, #8, #8 @ r10 = cluster
57
mov r3, #MAX_CPUS_PER_CLUSTER
58
mla r4, r3, r10, r9 @ r4 = canonical CPU index
59
cmp r4, #(MAX_CPUS_PER_CLUSTER * MAX_NR_CLUSTERS)
60
blo 2f
61
62
/* We didn't expect this CPU. Try to cheaply make it quiet. */
63
1: wfi
64
wfe
65
b 1b
66
67
2: pr_dbg "kernel mcpm_entry_point\n"
68
69
/*
70
* MMU is off so we need to get to various variables in a
71
* position independent way.
72
*/
73
adr r5, 3f
74
ldmia r5, {r0, r6, r7, r8, r11}
75
add r0, r5, r0 @ r0 = mcpm_entry_early_pokes
76
add r6, r5, r6 @ r6 = mcpm_entry_vectors
77
ldr r7, [r5, r7] @ r7 = mcpm_power_up_setup_phys
78
add r8, r5, r8 @ r8 = mcpm_sync
79
add r11, r5, r11 @ r11 = first_man_locks
80
81
@ Perform an early poke, if any
82
add r0, r0, r4, lsl #3
83
ldmia r0, {r0, r1}
84
teq r0, #0
85
strne r1, [r0]
86
87
mov r0, #MCPM_SYNC_CLUSTER_SIZE
88
mla r8, r0, r10, r8 @ r8 = sync cluster base
89
90
@ Signal that this CPU is coming UP:
91
mov r0, #CPU_COMING_UP
92
mov r5, #MCPM_SYNC_CPU_SIZE
93
mla r5, r9, r5, r8 @ r5 = sync cpu address
94
strb r0, [r5]
95
96
@ At this point, the cluster cannot unexpectedly enter the GOING_DOWN
97
@ state, because there is at least one active CPU (this CPU).
98
99
mov r0, #VLOCK_SIZE
100
mla r11, r0, r10, r11 @ r11 = cluster first man lock
101
mov r0, r11
102
mov r1, r9 @ cpu
103
bl vlock_trylock @ implies DMB
104
105
cmp r0, #0 @ failed to get the lock?
106
bne mcpm_setup_wait @ wait for cluster setup if so
107
108
ldrb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
109
cmp r0, #CLUSTER_UP @ cluster already up?
110
bne mcpm_setup @ if not, set up the cluster
111
112
@ Otherwise, release the first man lock and skip setup:
113
mov r0, r11
114
bl vlock_unlock
115
b mcpm_setup_complete
116
117
mcpm_setup:
118
@ Control dependency implies strb not observable before previous ldrb.
119
120
@ Signal that the cluster is being brought up:
121
mov r0, #INBOUND_COMING_UP
122
strb r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
123
dmb
124
125
@ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this
126
@ point onwards will observe INBOUND_COMING_UP and abort.
127
128
@ Wait for any previously-pending cluster teardown operations to abort
129
@ or complete:
130
mcpm_teardown_wait:
131
ldrb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
132
cmp r0, #CLUSTER_GOING_DOWN
133
bne first_man_setup
134
wfe
135
b mcpm_teardown_wait
136
137
first_man_setup:
138
dmb
139
140
@ If the outbound gave up before teardown started, skip cluster setup:
141
142
cmp r0, #CLUSTER_UP
143
beq mcpm_setup_leave
144
145
@ power_up_setup is now responsible for setting up the cluster:
146
147
cmp r7, #0
148
mov r0, #1 @ second (cluster) affinity level
149
blxne r7 @ Call power_up_setup if defined
150
dmb
151
152
mov r0, #CLUSTER_UP
153
strb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
154
dmb
155
156
mcpm_setup_leave:
157
@ Leave the cluster setup critical section:
158
159
mov r0, #INBOUND_NOT_COMING_UP
160
strb r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
161
dsb st
162
sev
163
164
mov r0, r11
165
bl vlock_unlock @ implies DMB
166
b mcpm_setup_complete
167
168
@ In the contended case, non-first men wait here for cluster setup
169
@ to complete:
170
mcpm_setup_wait:
171
ldrb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
172
cmp r0, #CLUSTER_UP
173
wfene
174
bne mcpm_setup_wait
175
dmb
176
177
mcpm_setup_complete:
178
@ If a platform-specific CPU setup hook is needed, it is
179
@ called from here.
180
181
cmp r7, #0
182
mov r0, #0 @ first (CPU) affinity level
183
blxne r7 @ Call power_up_setup if defined
184
dmb
185
186
@ Mark the CPU as up:
187
188
mov r0, #CPU_UP
189
strb r0, [r5]
190
191
@ Observability order of CPU_UP and opening of the gate does not matter.
192
193
mcpm_entry_gated:
194
ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector
195
cmp r5, #0
196
wfeeq
197
beq mcpm_entry_gated
198
dmb
199
200
pr_dbg "released\n"
201
bx r5
202
203
.align 2
204
205
3: .word mcpm_entry_early_pokes - .
206
.word mcpm_entry_vectors - 3b
207
.word mcpm_power_up_setup_phys - 3b
208
.word mcpm_sync - 3b
209
.word first_man_locks - 3b
210
211
ENDPROC(mcpm_entry_point)
212
213
.bss
214
215
.align CACHE_WRITEBACK_ORDER
216
.type first_man_locks, #object
217
first_man_locks:
218
.space VLOCK_SIZE * MAX_NR_CLUSTERS
219
.align CACHE_WRITEBACK_ORDER
220
221
.type mcpm_entry_vectors, #object
222
ENTRY(mcpm_entry_vectors)
223
.space 4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
224
225
.type mcpm_entry_early_pokes, #object
226
ENTRY(mcpm_entry_early_pokes)
227
.space 8 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
228
229
.type mcpm_power_up_setup_phys, #object
230
ENTRY(mcpm_power_up_setup_phys)
231
.space 4 @ set by mcpm_sync_init()
232
233