 2098990e7c
			
		
	
	
	2098990e7c
	
	
	
		
			
			Conflicts: arch/arm/kernel/head.S This series has been well tested and it would be great to get this merged now. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
		
			
				
	
	
		
			233 lines
		
	
	
	
		
			5.3 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
	
		
			5.3 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /*
 | |
|  * arch/arm/common/mcpm_head.S -- kernel entry point for multi-cluster PM
 | |
|  *
 | |
|  * Created by:  Nicolas Pitre, March 2012
 | |
|  * Copyright:   (C) 2012-2013  Linaro Limited
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License version 2 as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  *
 | |
|  * Refer to Documentation/arm/cluster-pm-race-avoidance.txt
 | |
|  * for details of the synchronisation algorithms used here.
 | |
|  */
 | |
| 
 | |
| #include <linux/linkage.h>
 | |
| #include <asm/mcpm.h>
 | |
| #include <asm/assembler.h>
 | |
| 
 | |
| #include "vlock.h"
 | |
| 
 | |
| .if MCPM_SYNC_CLUSTER_CPUS
 | |
| .error "cpus must be the first member of struct mcpm_sync_struct"
 | |
| .endif
 | |
| 
 | |
| 	.macro	pr_dbg	string
 | |
| #if defined(CONFIG_DEBUG_LL) && defined(DEBUG)
 | |
| 	b	1901f
 | |
| 1902:	.asciz	"CPU"
 | |
| 1903:	.asciz	" cluster"
 | |
| 1904:	.asciz	": \string"
 | |
| 	.align
 | |
| 1901:	adr	r0, 1902b
 | |
| 	bl	printascii
 | |
| 	mov	r0, r9
 | |
| 	bl	printhex2
 | |
| 	adr	r0, 1903b
 | |
| 	bl	printascii
 | |
| 	mov	r0, r10
 | |
| 	bl	printhex2
 | |
| 	adr	r0, 1904b
 | |
| 	bl	printascii
 | |
| #endif
 | |
| 	.endm
 | |
| 
 | |
| 	.arm
 | |
| 	.align
 | |
| 
 | |
| ENTRY(mcpm_entry_point)
 | |
| 
 | |
|  ARM_BE8(setend        be)
 | |
|  THUMB(	adr	r12, BSYM(1f)	)
 | |
|  THUMB(	bx	r12		)
 | |
|  THUMB(	.thumb			)
 | |
| 1:
 | |
| 	mrc	p15, 0, r0, c0, c0, 5		@ MPIDR
 | |
| 	ubfx	r9, r0, #0, #8			@ r9 = cpu
 | |
| 	ubfx	r10, r0, #8, #8			@ r10 = cluster
 | |
| 	mov	r3, #MAX_CPUS_PER_CLUSTER
 | |
| 	mla	r4, r3, r10, r9			@ r4 = canonical CPU index
 | |
| 	cmp	r4, #(MAX_CPUS_PER_CLUSTER * MAX_NR_CLUSTERS)
 | |
| 	blo	2f
 | |
| 
 | |
| 	/* We didn't expect this CPU.  Try to cheaply make it quiet. */
 | |
| 1:	wfi
 | |
| 	wfe
 | |
| 	b	1b
 | |
| 
 | |
| 2:	pr_dbg	"kernel mcpm_entry_point\n"
 | |
| 
 | |
| 	/*
 | |
| 	 * MMU is off so we need to get to various variables in a
 | |
| 	 * position independent way.
 | |
| 	 */
 | |
| 	adr	r5, 3f
 | |
| 	ldmia	r5, {r0, r6, r7, r8, r11}
 | |
| 	add	r0, r5, r0			@ r0 = mcpm_entry_early_pokes
 | |
| 	add	r6, r5, r6			@ r6 = mcpm_entry_vectors
 | |
| 	ldr	r7, [r5, r7]			@ r7 = mcpm_power_up_setup_phys
 | |
| 	add	r8, r5, r8			@ r8 = mcpm_sync
 | |
| 	add	r11, r5, r11			@ r11 = first_man_locks
 | |
| 
 | |
| 	@ Perform an early poke, if any
 | |
| 	add	r0, r0, r4, lsl #3
 | |
| 	ldmia	r0, {r0, r1}
 | |
| 	teq	r0, #0
 | |
| 	strne	r1, [r0]
 | |
| 
 | |
| 	mov	r0, #MCPM_SYNC_CLUSTER_SIZE
 | |
| 	mla	r8, r0, r10, r8			@ r8 = sync cluster base
 | |
| 
 | |
| 	@ Signal that this CPU is coming UP:
 | |
| 	mov	r0, #CPU_COMING_UP
 | |
| 	mov	r5, #MCPM_SYNC_CPU_SIZE
 | |
| 	mla	r5, r9, r5, r8			@ r5 = sync cpu address
 | |
| 	strb	r0, [r5]
 | |
| 
 | |
| 	@ At this point, the cluster cannot unexpectedly enter the GOING_DOWN
 | |
| 	@ state, because there is at least one active CPU (this CPU).
 | |
| 
 | |
| 	mov	r0, #VLOCK_SIZE
 | |
| 	mla	r11, r0, r10, r11		@ r11 = cluster first man lock
 | |
| 	mov	r0, r11
 | |
| 	mov	r1, r9				@ cpu
 | |
| 	bl	vlock_trylock			@ implies DMB
 | |
| 
 | |
| 	cmp	r0, #0				@ failed to get the lock?
 | |
| 	bne	mcpm_setup_wait		@ wait for cluster setup if so
 | |
| 
 | |
| 	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
 | |
| 	cmp	r0, #CLUSTER_UP			@ cluster already up?
 | |
| 	bne	mcpm_setup			@ if not, set up the cluster
 | |
| 
 | |
| 	@ Otherwise, release the first man lock and skip setup:
 | |
| 	mov	r0, r11
 | |
| 	bl	vlock_unlock
 | |
| 	b	mcpm_setup_complete
 | |
| 
 | |
| mcpm_setup:
 | |
| 	@ Control dependency implies strb not observable before previous ldrb.
 | |
| 
 | |
| 	@ Signal that the cluster is being brought up:
 | |
| 	mov	r0, #INBOUND_COMING_UP
 | |
| 	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
 | |
| 	dmb
 | |
| 
 | |
| 	@ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this
 | |
| 	@ point onwards will observe INBOUND_COMING_UP and abort.
 | |
| 
 | |
| 	@ Wait for any previously-pending cluster teardown operations to abort
 | |
| 	@ or complete:
 | |
| mcpm_teardown_wait:
 | |
| 	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
 | |
| 	cmp	r0, #CLUSTER_GOING_DOWN
 | |
| 	bne	first_man_setup
 | |
| 	wfe
 | |
| 	b	mcpm_teardown_wait
 | |
| 
 | |
| first_man_setup:
 | |
| 	dmb
 | |
| 
 | |
| 	@ If the outbound gave up before teardown started, skip cluster setup:
 | |
| 
 | |
| 	cmp	r0, #CLUSTER_UP
 | |
| 	beq	mcpm_setup_leave
 | |
| 
 | |
| 	@ power_up_setup is now responsible for setting up the cluster:
 | |
| 
 | |
| 	cmp	r7, #0
 | |
| 	mov	r0, #1		@ second (cluster) affinity level
 | |
| 	blxne	r7		@ Call power_up_setup if defined
 | |
| 	dmb
 | |
| 
 | |
| 	mov	r0, #CLUSTER_UP
 | |
| 	strb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
 | |
| 	dmb
 | |
| 
 | |
| mcpm_setup_leave:
 | |
| 	@ Leave the cluster setup critical section:
 | |
| 
 | |
| 	mov	r0, #INBOUND_NOT_COMING_UP
 | |
| 	strb	r0, [r8, #MCPM_SYNC_CLUSTER_INBOUND]
 | |
| 	dsb	st
 | |
| 	sev
 | |
| 
 | |
| 	mov	r0, r11
 | |
| 	bl	vlock_unlock	@ implies DMB
 | |
| 	b	mcpm_setup_complete
 | |
| 
 | |
| 	@ In the contended case, non-first men wait here for cluster setup
 | |
| 	@ to complete:
 | |
| mcpm_setup_wait:
 | |
| 	ldrb	r0, [r8, #MCPM_SYNC_CLUSTER_CLUSTER]
 | |
| 	cmp	r0, #CLUSTER_UP
 | |
| 	wfene
 | |
| 	bne	mcpm_setup_wait
 | |
| 	dmb
 | |
| 
 | |
| mcpm_setup_complete:
 | |
| 	@ If a platform-specific CPU setup hook is needed, it is
 | |
| 	@ called from here.
 | |
| 
 | |
| 	cmp	r7, #0
 | |
| 	mov	r0, #0		@ first (CPU) affinity level
 | |
| 	blxne	r7		@ Call power_up_setup if defined
 | |
| 	dmb
 | |
| 
 | |
| 	@ Mark the CPU as up:
 | |
| 
 | |
| 	mov	r0, #CPU_UP
 | |
| 	strb	r0, [r5]
 | |
| 
 | |
| 	@ Observability order of CPU_UP and opening of the gate does not matter.
 | |
| 
 | |
| mcpm_entry_gated:
 | |
| 	ldr	r5, [r6, r4, lsl #2]		@ r5 = CPU entry vector
 | |
| 	cmp	r5, #0
 | |
| 	wfeeq
 | |
| 	beq	mcpm_entry_gated
 | |
| 	dmb
 | |
| 
 | |
| 	pr_dbg	"released\n"
 | |
| 	bx	r5
 | |
| 
 | |
| 	.align	2
 | |
| 
 | |
| 3:	.word	mcpm_entry_early_pokes - .
 | |
| 	.word	mcpm_entry_vectors - 3b
 | |
| 	.word	mcpm_power_up_setup_phys - 3b
 | |
| 	.word	mcpm_sync - 3b
 | |
| 	.word	first_man_locks - 3b
 | |
| 
 | |
| ENDPROC(mcpm_entry_point)
 | |
| 
 | |
| 	.bss
 | |
| 
 | |
| 	.align	CACHE_WRITEBACK_ORDER
 | |
| 	.type	first_man_locks, #object
 | |
| first_man_locks:
 | |
| 	.space	VLOCK_SIZE * MAX_NR_CLUSTERS
 | |
| 	.align	CACHE_WRITEBACK_ORDER
 | |
| 
 | |
| 	.type	mcpm_entry_vectors, #object
 | |
| ENTRY(mcpm_entry_vectors)
 | |
| 	.space	4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
 | |
| 
 | |
| 	.type	mcpm_entry_early_pokes, #object
 | |
| ENTRY(mcpm_entry_early_pokes)
 | |
| 	.space	8 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER
 | |
| 
 | |
| 	.type	mcpm_power_up_setup_phys, #object
 | |
| ENTRY(mcpm_power_up_setup_phys)
 | |
| 	.space  4		@ set by mcpm_sync_init()
 |