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