root/arch/arm/common/mcpm_head.S

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

/* [<][>][^][v][top][bottom][index][help] */