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