mcpm_head.S 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * arch/arm/common/mcpm_head.S -- kernel entry point for multi-cluster PM
  3. *
  4. * Created by: Nicolas Pitre, March 2012
  5. * Copyright: (C) 2012-2013 Linaro Limited
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. *
  12. * Refer to Documentation/arm/cluster-pm-race-avoidance.txt
  13. * for details of the synchronisation algorithms used here.
  14. */
  15. #include <linux/linkage.h>
  16. #include <asm/mcpm.h>
  17. .if MCPM_SYNC_CLUSTER_CPUS
  18. .error "cpus must be the first member of struct mcpm_sync_struct"
  19. .endif
  20. .macro pr_dbg string
  21. #if defined(CONFIG_DEBUG_LL) && defined(DEBUG)
  22. b 1901f
  23. 1902: .asciz "CPU"
  24. 1903: .asciz " cluster"
  25. 1904: .asciz ": \string"
  26. .align
  27. 1901: adr r0, 1902b
  28. bl printascii
  29. mov r0, r9
  30. bl printhex8
  31. adr r0, 1903b
  32. bl printascii
  33. mov r0, r10
  34. bl printhex8
  35. adr r0, 1904b
  36. bl printascii
  37. #endif
  38. .endm
  39. .arm
  40. .align
  41. ENTRY(mcpm_entry_point)
  42. THUMB( adr r12, BSYM(1f) )
  43. THUMB( bx r12 )
  44. THUMB( .thumb )
  45. 1:
  46. mrc p15, 0, r0, c0, c0, 5 @ MPIDR
  47. ubfx r9, r0, #0, #8 @ r9 = cpu
  48. ubfx r10, r0, #8, #8 @ r10 = cluster
  49. mov r3, #MAX_CPUS_PER_CLUSTER
  50. mla r4, r3, r10, r9 @ r4 = canonical CPU index
  51. cmp r4, #(MAX_CPUS_PER_CLUSTER * MAX_NR_CLUSTERS)
  52. blo 2f
  53. /* We didn't expect this CPU. Try to cheaply make it quiet. */
  54. 1: wfi
  55. wfe
  56. b 1b
  57. 2: pr_dbg "kernel mcpm_entry_point\n"
  58. /*
  59. * MMU is off so we need to get to various variables in a
  60. * position independent way.
  61. */
  62. adr r5, 3f
  63. ldmia r5, {r6, r7, r8}
  64. add r6, r5, r6 @ r6 = mcpm_entry_vectors
  65. ldr r7, [r5, r7] @ r7 = mcpm_power_up_setup_phys
  66. add r8, r5, r8 @ r8 = mcpm_sync
  67. mov r0, #MCPM_SYNC_CLUSTER_SIZE
  68. mla r8, r0, r10, r8 @ r8 = sync cluster base
  69. @ Signal that this CPU is coming UP:
  70. mov r0, #CPU_COMING_UP
  71. mov r5, #MCPM_SYNC_CPU_SIZE
  72. mla r5, r9, r5, r8 @ r5 = sync cpu address
  73. strb r0, [r5]
  74. @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN
  75. @ state, because there is at least one active CPU (this CPU).
  76. @ Note: the following is racy as another CPU might be testing
  77. @ the same flag at the same moment. That'll be fixed later.
  78. ldrb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
  79. cmp r0, #CLUSTER_UP @ cluster already up?
  80. bne mcpm_setup @ if not, set up the cluster
  81. @ Otherwise, skip setup:
  82. b mcpm_setup_complete
  83. mcpm_setup:
  84. @ Control dependency implies strb not observable before previous ldrb.
  85. @ Signal that the cluster is being brought up:
  86. mov r0, #INBOUND_COMING_UP
  87. strb r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
  88. dmb
  89. @ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this
  90. @ point onwards will observe INBOUND_COMING_UP and abort.
  91. @ Wait for any previously-pending cluster teardown operations to abort
  92. @ or complete:
  93. mcpm_teardown_wait:
  94. ldrb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
  95. cmp r0, #CLUSTER_GOING_DOWN
  96. bne first_man_setup
  97. wfe
  98. b mcpm_teardown_wait
  99. first_man_setup:
  100. dmb
  101. @ If the outbound gave up before teardown started, skip cluster setup:
  102. cmp r0, #CLUSTER_UP
  103. beq mcpm_setup_leave
  104. @ power_up_setup is now responsible for setting up the cluster:
  105. cmp r7, #0
  106. mov r0, #1 @ second (cluster) affinity level
  107. blxne r7 @ Call power_up_setup if defined
  108. dmb
  109. mov r0, #CLUSTER_UP
  110. strb r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
  111. dmb
  112. mcpm_setup_leave:
  113. @ Leave the cluster setup critical section:
  114. mov r0, #INBOUND_NOT_COMING_UP
  115. strb r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
  116. dsb
  117. sev
  118. mcpm_setup_complete:
  119. @ If a platform-specific CPU setup hook is needed, it is
  120. @ called from here.
  121. cmp r7, #0
  122. mov r0, #0 @ first (CPU) affinity level
  123. blxne r7 @ Call power_up_setup if defined
  124. dmb
  125. @ Mark the CPU as up:
  126. mov r0, #CPU_UP
  127. strb r0, [r5]
  128. @ Observability order of CPU_UP and opening of the gate does not matter.
  129. mcpm_entry_gated:
  130. ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector
  131. cmp r5, #0
  132. wfeeq
  133. beq mcpm_entry_gated
  134. dmb
  135. pr_dbg "released\n"
  136. bx r5
  137. .align 2
  138. 3: .word mcpm_entry_vectors - .
  139. .word mcpm_power_up_setup_phys - 3b
  140. .word mcpm_sync - 3b
  141. ENDPROC(mcpm_entry_point)
  142. .bss
  143. .align 5
  144. .type mcpm_entry_vectors, #object
  145. ENTRY(mcpm_entry_vectors)
  146. .space 4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
  147. .type mcpm_power_up_setup_phys, #object
  148. ENTRY(mcpm_power_up_setup_phys)
  149. .space 4 @ set by mcpm_sync_init()